Improving state machine timing, logged material, comments, etc
[theonering] / src / requests.py
1 import logging
2
3 import dbus
4 import telepathy
5 import gtk_toolbox
6
7
8 _moduleLogger = logging.getLogger('requests')
9
10
11 class RequestsMixin(
12         telepathy._generated.Connection_Interface_Requests.ConnectionInterfaceRequests,
13         telepathy.server.properties.DBusProperties
14 ):
15         """
16         HACK older python-telepathy doesn't provide an implementation but the new one does, ARGH
17         """
18
19         def __init__(self):
20                 telepathy._generated.Connection_Interface_Requests.ConnectionInterfaceRequests.__init__(self)
21                 telepathy.server.properties.DBusProperties.__init__(self)
22
23                 self._implement_property_get(telepathy.interfaces.CONNECTION_INTERFACE_REQUESTS,
24                         {'Channels': lambda: dbus.Array(self._get_channels(),
25                                 signature='(oa{sv})'),
26                         'RequestableChannelClasses': lambda: dbus.Array(
27                                 self._channel_manager.get_requestable_channel_classes(),
28                                 signature='(a{sv}as)')})
29
30         @property
31         def _channel_manager(self):
32                 """
33                 @abstract
34                 """
35                 raise NotImplementedError("Abstract property called")
36
37         def _get_channels(self):
38                 return [(c._object_path, c.get_props()) for c in self._channels]
39
40         def _check_basic_properties(self, props):
41                 # ChannelType must be present and must be a string.
42                 if telepathy.interfaces.CHANNEL_INTERFACE + '.ChannelType' not in props or \
43                                 not isinstance(props[telepathy.interfaces.CHANNEL_INTERFACE + '.ChannelType'],
44                                         dbus.String):
45                         raise telepathy.errors.InvalidArgument('ChannelType is required')
46
47                 def check_valid_type_if_exists(prop, fun):
48                         p = telepathy.interfaces.CHANNEL_INTERFACE + '.' + prop
49                         if p in props and not fun(props[p]):
50                                 raise telepathy.errors.InvalidArgument('Invalid %s' % prop)
51
52                 # Allow TargetHandleType to be missing, but not to be otherwise broken.
53                 check_valid_type_if_exists('TargetHandleType',
54                         lambda p: p > 0 and p < (2**32)-1)
55
56                 # Allow TargetType to be missing, but not to be otherwise broken.
57                 check_valid_type_if_exists('TargetHandle',
58                         lambda p: p > 0 and p < (2**32)-1)
59                 if props.get(telepathy.interfaces.CHANNEL_INTERFACE + '.TargetHandle') == 0:
60                         raise telepathy.errors.InvalidArgument("TargetHandle may not be 0")
61
62                 # Allow TargetID to be missing, but not to be otherwise broken.
63                 check_valid_type_if_exists('TargetID',
64                         lambda p: isinstance(p, dbus.String))
65
66                 # Disallow InitiatorHandle, InitiatorID and Requested.
67                 check_valid_type_if_exists('InitiatorHandle', lambda p: False)
68                 check_valid_type_if_exists('InitiatorID', lambda p: False)
69                 check_valid_type_if_exists('Requested', lambda p: False)
70
71                 type = props[telepathy.interfaces.CHANNEL_INTERFACE + '.ChannelType']
72                 handle_type = props.get(telepathy.interfaces.CHANNEL_INTERFACE + '.TargetHandleType',
73                                 telepathy.constants.HANDLE_TYPE_NONE)
74                 handle = props.get(telepathy.interfaces.CHANNEL_INTERFACE + '.TargetHandle', 0)
75
76                 return (type, handle_type, handle)
77
78         def _validate_handle(self, props):
79                 target_handle_type = props.get(telepathy.interfaces.CHANNEL_INTERFACE + '.TargetHandleType',
80                         telepathy.constants.HANDLE_TYPE_NONE)
81                 target_handle = props.get(telepathy.interfaces.CHANNEL_INTERFACE + '.TargetHandle', None)
82                 target_id = props.get(telepathy.interfaces.CHANNEL_INTERFACE + '.TargetID', None)
83
84                 # Handle type 0 cannot have a handle.
85                 if target_handle_type == telepathy.constants.HANDLE_TYPE_NONE and target_handle != None:
86                         raise telepathy.errors.InvalidArgument('When TargetHandleType is NONE, ' +
87                                 'TargetHandle must be omitted')
88
89                 # Handle type 0 cannot have a TargetID.
90                 if target_handle_type == telepathy.constants.HANDLE_TYPE_NONE and target_id != None:
91                         raise telepathy.errors.InvalidArgument('When TargetHandleType is NONE, TargetID ' +
92                                 'must be omitted')
93
94                 if target_handle_type != telepathy.constants.HANDLE_TYPE_NONE:
95                         if target_handle == None and target_id == None:
96                                 raise telepathy.errors.InvalidArgument('When TargetHandleType is not NONE, ' +
97                                         'either TargetHandle or TargetID must also be given')
98
99                         if target_handle != None and target_id != None:
100                                 raise telepathy.errors.InvalidArgument('TargetHandle and TargetID must not ' +
101                                         'both be given')
102
103                         self.check_handle_type(target_handle_type)
104
105
106         def _alter_properties(self, props):
107                 target_handle_type = props.get(telepathy.interfaces.CHANNEL_INTERFACE + '.TargetHandleType',
108                         telepathy.constants.HANDLE_TYPE_NONE)
109                 target_handle = props.get(telepathy.interfaces.CHANNEL_INTERFACE + '.TargetHandle', None)
110                 target_id = props.get(telepathy.interfaces.CHANNEL_INTERFACE + '.TargetID', None)
111
112                 altered_properties = props.copy()
113
114                 if target_handle_type != telepathy.constants.HANDLE_TYPE_NONE:
115                         if target_handle == None:
116                                 # Turn TargetID into TargetHandle.
117                                 for handle in self._handles.itervalues():
118                                         if handle.get_name() == target_id and handle.get_type() == target_handle_type:
119                                                 target_handle = handle.get_id()
120                                 if not target_handle:
121                                         raise telepathy.errors.InvalidHandle('TargetID %s not valid for type %d' %
122                                                 target_id, target_handle_type)
123
124                                 altered_properties[telepathy.interfaces.CHANNEL_INTERFACE + '.TargetHandle'] = \
125                                         target_handle
126                         else:
127                                 # Check the supplied TargetHandle is valid
128                                 self.check_handle(target_handle_type, target_handle)
129
130                                 target_id = self._handles[target_handle_type,\
131                                                                                         target_handle].get_name()
132                                 altered_properties[telepathy.interfaces.CHANNEL_INTERFACE + '.TargetID'] = \
133                                         target_id
134
135                 altered_properties[telepathy.interfaces.CHANNEL_INTERFACE + '.Requested'] = True
136
137                 return altered_properties
138
139         @dbus.service.method(telepathy.interfaces.CONNECTION_INTERFACE_REQUESTS,
140                 in_signature='a{sv}', out_signature='oa{sv}',
141                 async_callbacks=('_success', '_error'))
142         def CreateChannel(self, request, _success, _error):
143                 _moduleLogger.info("CreateChannel")
144                 type, handle_type, handle = self._check_basic_properties(request)
145                 self._validate_handle(request)
146                 props = self._alter_properties(request)
147
148                 channel = self._channel_manager.channel_for_props(props, signal=False)
149
150                 # Remove mutable properties
151                 todel = []
152                 for prop in props:
153                         iface, name = prop.rsplit('.', 1) # a bit of a hack
154                         if name in channel._immutable_properties:
155                                 if channel._immutable_properties[name] != iface:
156                                         todel.append(prop)
157                         else:
158                                 todel.append(prop)
159
160                 for p in todel:
161                         del props[p]
162
163                 _success(channel._object_path, props)
164
165                 # CreateChannel MUST return *before* NewChannels is emitted.
166                 # @bug On older python-telepathy, it doesn't exist
167                 self.signal_new_channels([channel])
168
169         @dbus.service.method(telepathy.interfaces.CONNECTION_INTERFACE_REQUESTS,
170                 in_signature='a{sv}', out_signature='boa{sv}',
171                 async_callbacks=('_success', '_error'))
172         def EnsureChannel(self, request, _success, _error):
173                 _moduleLogger.info("EnsureChannel")
174                 type, handle_type, handle = self._check_basic_properties(request)
175                 self._validate_handle(request)
176                 props = self._alter_properties(request)
177
178                 yours = not self._channel_manager.channel_exists(props)
179
180                 channel = self._channel_manager.channel_for_props(props, signal=False)
181
182                 _success(yours, channel._object_path, props)
183
184                 # @bug On older python-telepathy, it doesn't exist
185                 self.signal_new_channels([channel])