diff options
-rw-r--r-- | sunshine/capabilities.py | 229 | ||||
-rw-r--r-- | sunshine/connection.py | 16 | ||||
-rw-r--r-- | sunshine/contacts.py | 28 |
3 files changed, 221 insertions, 52 deletions
diff --git a/sunshine/capabilities.py b/sunshine/capabilities.py index 4b1e529..e74759a 100644 --- a/sunshine/capabilities.py +++ b/sunshine/capabilities.py @@ -22,18 +22,54 @@ import dbus import telepathy +from telepathy._generated.Connection_Interface_Contact_Capabilities \ + import ConnectionInterfaceContactCapabilities + +from butterfly.util.decorator import async from sunshine.handle import SunshineHandleFactory __all__ = ['SunshineCapabilities'] logger = logging.getLogger('Sunshine.Capabilities') -class SunshineCapabilities(telepathy.server.ConnectionInterfaceCapabilities): +class SunshineCapabilities(telepathy.server.ConnectionInterfaceCapabilities, + ConnectionInterfaceContactCapabilities): + + text_chat_class = \ + ({telepathy.CHANNEL_INTERFACE + '.ChannelType': + telepathy.CHANNEL_TYPE_TEXT, + telepathy.CHANNEL_INTERFACE + '.TargetHandleType': + dbus.UInt32(telepathy.HANDLE_TYPE_CONTACT)}, + [telepathy.CHANNEL_INTERFACE + '.TargetHandle', + telepathy.CHANNEL_INTERFACE + '.TargetID']) + + audio_chat_class = \ + ({telepathy.CHANNEL_INTERFACE + '.ChannelType': + telepathy.CHANNEL_TYPE_STREAMED_MEDIA, + telepathy.CHANNEL_INTERFACE + '.TargetHandleType': + dbus.UInt32(telepathy.HANDLE_TYPE_CONTACT)}, + [telepathy.CHANNEL_INTERFACE + '.TargetHandle', + telepathy.CHANNEL_INTERFACE + '.TargetID', + telepathy.CHANNEL_TYPE_STREAMED_MEDIA + '.InitialAudio']) + + av_chat_class = \ + ({telepathy.CHANNEL_INTERFACE + '.ChannelType': + telepathy.CHANNEL_TYPE_STREAMED_MEDIA, + telepathy.CHANNEL_INTERFACE + '.TargetHandleType': + dbus.UInt32(telepathy.HANDLE_TYPE_CONTACT)}, + [telepathy.CHANNEL_INTERFACE + '.TargetHandle', + telepathy.CHANNEL_INTERFACE + '.TargetID', + telepathy.CHANNEL_TYPE_STREAMED_MEDIA + '.InitialAudio', + telepathy.CHANNEL_TYPE_STREAMED_MEDIA + '.InitialVideo']) def __init__(self): telepathy.server.ConnectionInterfaceCapabilities.__init__(self) -# papyon.event.ContactEventInterface.__init__(self, self.msn_client) - dbus_interface = telepathy.CONNECTION_INTERFACE_CAPABILITIES + ConnectionInterfaceContactCapabilities.__init__(self) + + # handle -> list(RCC) + self._contact_caps = {} + self._video_clients = [] + self._update_capabilities_calls = [] def AdvertiseCapabilities(self, add, remove): # for caps, specs in add: @@ -48,40 +84,165 @@ class SunshineCapabilities(telepathy.server.ConnectionInterfaceCapabilities): return telepathy.server.ConnectionInterfaceCapabilities.\ AdvertiseCapabilities(self, add, remove) -# # papyon.event.ContactEventInterface -# def on_contact_client_capabilities_changed(self, contact): -# self._update_capabilities(contact) -# -# def _update_capabilities(self, contact): -# handle = ButterflyHandleFactory(self, 'contact', -# contact.account, contact.network_id) -# ctype = telepathy.CHANNEL_TYPE_STREAMED_MEDIA -# -# new_gen, new_spec = self._get_capabilities(contact) -# if handle in self._caps: -# old_gen, old_spec = self._caps[handle][ctype] -# else: -# old_gen = 0 -# old_spec = 0 -# -# if old_gen == new_gen and old_spec == new_spec: -# return -# -# diff = (int(handle), ctype, old_gen, new_gen, old_spec, new_spec) -# self.CapabilitiesChanged([diff]) -# + + def GetContactCapabilities(self, handles): + print handles + if 0 in handles: + raise telepathy.InvalidHandle('Contact handle list contains zero') + + ret = dbus.Dictionary({}, signature='ua(a{sv}as)') + for i in handles: + handle = self.handle(telepathy.HANDLE_TYPE_CONTACT, i) + ret[handle] = self._contact_caps[handle] + + return ret + + def UpdateCapabilities(self, caps): + if self._state != telepathy.CONNECTION_STATUS_CONNECTED: + self._update_capabilities_calls.append(caps) + return + + # voip is disabled, so + return + + # We only care about voip. + for client, classes, capabilities in caps: + video = False + for channel_class in classes: + # Does this client support video? + if channel_class[telepathy.CHANNEL_INTERFACE + '.ChannelType'] == \ + telepathy.CHANNEL_TYPE_STREAMED_MEDIA: + video = True + self._video_clients.append(client) + else: + # *Did* it used to support video? + if client in self._video_clients: + self._video_clients.remove(client) + + changed = False + + # We've got no more clients that support video; remove the cap. + if not video and not self._video_clients: + self._self_handle.profile.client_id.has_webcam = False + changed = True + + # We want video. + if video and not self._self_handle.profile.client_id.has_webcam: + self._self_handle.profile.client_id.has_webcam = True + self._self_handle.profile.client_id.supports_rtc_video = True + changed = True + + # Signal. + if changed: + updated = dbus.Dictionary({self._self_handle: self._contact_caps[self._self_handle]}, + signature='ua(a{sv}as)') + self.ContactCapabilitiesChanged(updated) + + # papyon.event.ContactEventInterface + def on_contact_client_capabilities_changed(self, contact): + self._update_capabilities(contact) + + # papyon.event.AddressBookEventInterface + def on_contact_added(self, contact): + """When we add a contact in our contact list, add the + capabilities to create text channel to the contact""" + handle = SunshineHandleFactory(self._conn_ref(), 'contact', + str(contact), None) + self.add_text_capabilities([handle]) + + def add_text_capabilities(self, contacts_handles): + """Add the create capability for text channel to these contacts.""" + ret = [] + cc_ret = dbus.Dictionary({}, signature='ua(a{sv}as)') + for handle in contacts_handles: + ctype = telepathy.CHANNEL_TYPE_TEXT + if handle in self._caps: + old_gen, old_spec = self._caps[handle][ctype] + else: + old_gen = 0 + old_spec = 0 + new_gen = old_gen + new_gen |= telepathy.CONNECTION_CAPABILITY_FLAG_CREATE + + diff = (int(handle), ctype, old_gen, new_gen, old_spec, old_spec) + ret.append(diff) + + # ContactCapabilities + self._contact_caps.setdefault(handle, []).append(self.text_chat_class) + cc_ret[handle] = self._contact_caps[handle] + + self.CapabilitiesChanged(ret) + self.ContactCapabilitiesChanged(cc_ret) + + def _update_capabilities(self, contact): + handle = SunshineHandleFactory(self, 'contact', + contact.account, contact.network_id) + ctype = telepathy.CHANNEL_TYPE_STREAMED_MEDIA + + new_gen, new_spec, rcc = self._get_capabilities(contact) + if handle in self._caps: + old_gen, old_spec = self._caps[handle][ctype] + else: + old_gen = 0 + old_spec = 0 + + if old_gen != new_gen or old_spec != new_spec: + diff = (int(handle), ctype, old_gen, new_gen, old_spec, new_spec) + self.CapabilitiesChanged([diff]) + + if rcc is None: + return + + self._contact_caps.setdefault(handle, []) + + if rcc in self._contact_caps[handle]: + return + + if self.audio_chat_class in self._contact_caps[handle]: + self._contact_caps[handle].remove(self.audio_chat_class) + + if self.audio_chat_class in self._contact_caps[handle]: + self._contact_caps[handle].remove(self.audio_chat_class) + + self._contact_caps[handle].append(rcc) + + ret = dbus.Dictionary({handle: self._contact_caps[handle]}, + signature='ua(a{sv}as)') + self.ContactCapabilitiesChanged(ret) + def _get_capabilities(self, contact): gen_caps = 0 spec_caps = 0 + rcc = None + caps = contact.client_capabilities #if caps.supports_sip_invite: - # gen_caps |= telepathy.CONNECTION_CAPABILITY_FLAG_CREATE - # gen_caps |= telepathy.CONNECTION_CAPABILITY_FLAG_INVITE - # spec_caps |= telepathy.CHANNEL_MEDIA_CAPABILITY_AUDIO - # spec_caps |= telepathy.CHANNEL_MEDIA_CAPABILITY_NAT_TRAVERSAL_STUN - # - # if caps.has_webcam: - # spec_caps |= telepathy.CHANNEL_MEDIA_CAPABILITY_VIDEO - - return gen_caps, spec_caps + #gen_caps |= telepathy.CONNECTION_CAPABILITY_FLAG_CREATE + #gen_caps |= telepathy.CONNECTION_CAPABILITY_FLAG_INVITE + #spec_caps |= telepathy.CHANNEL_MEDIA_CAPABILITY_AUDIO + #spec_caps |= telepathy.CHANNEL_MEDIA_CAPABILITY_NAT_TRAVERSAL_STUN + + #if caps.has_webcam: + #spec_caps |= telepathy.CHANNEL_MEDIA_CAPABILITY_VIDEO + #rcc = self.av_chat_class + #else: + #rcc = self.audio_chat_class + + return gen_caps, spec_caps, rcc + + @async + def _populate_capabilities(self): + """ Add the capability to create text channels to all contacts in our + contacts list.""" + handles = set([self._self_handle]) + for contact in self.profile.contacts: + handle = SunshineHandleFactory(self, 'contact', + str(contact), None) + handles.add(handle) + self.add_text_capabilities(handles) + + # These caps were updated before we were online. + for caps in self._update_capabilities_calls: + self.UpdateCapabilities(caps) + self._update_capabilities_calls = [] diff --git a/sunshine/connection.py b/sunshine/connection.py index 11928dc..cbfbd4c 100644 --- a/sunshine/connection.py +++ b/sunshine/connection.py @@ -286,8 +286,7 @@ class SunshineConnection(telepathy.server.Connection, telepathy.server.Connection.__init__(self, 'gadugadu', account, 'sunshine') telepathy.server.ConnectionInterfaceRequests.__init__(self) SunshinePresence.__init__(self) - self.avatars_interface = SunshineAvatars.__init__(self) - SunshineCapabilities.__init__(self) + SunshineAvatars.__init__(self) SunshineContacts.__init__(self) self.set_self_handle(SunshineHandleFactory(self, 'self')) @@ -504,6 +503,7 @@ class SunshineConnection(telepathy.server.Connection, self.makeTelepathyGroupChannels() SunshineAliasing.__init__(self) + SunshineCapabilities.__init__(self) self._status = telepathy.CONNECTION_STATUS_CONNECTED self.StatusChanged(telepathy.CONNECTION_STATUS_CONNECTED, @@ -528,10 +528,12 @@ class SunshineConnection(telepathy.server.Connection, self.makeTelepathyGroupChannels() SunshineAliasing.__init__(self) + SunshineCapabilities.__init__(self) self._status = telepathy.CONNECTION_STATUS_CONNECTED self.StatusChanged(telepathy.CONNECTION_STATUS_CONNECTED, telepathy.CONNECTION_STATUS_REASON_REQUESTED) + self._populate_capabilities() def on_loginFailed(self, response): logger.info("Login failed: ", response) @@ -552,14 +554,14 @@ class SunshineConnection(telepathy.server.Connection, if hasattr(msg.content.attrs, 'conference') and msg.content.attrs.conference != None: recipients = msg.content.attrs.conference.recipients #recipients.append(self.profile.uin) - print msg.sender - print 'recipients:', recipients + #print msg.sender + #print 'recipients:', recipients recipients = map(str, recipients) recipients.append(str(msg.sender)) - print 'recipients:', recipients + #print 'recipients:', recipients recipients = sorted(recipients) conf_name = ', '.join(map(str, recipients)) - print 'conf_name:', conf_name + #print 'conf_name:', conf_name #active handle for current writting contact ahandle_id = self.get_handle_id_by_name(telepathy.constants.HANDLE_TYPE_CONTACT, @@ -573,7 +575,7 @@ class SunshineConnection(telepathy.server.Connection, #now we need to preapare a new room and make initial users in it room_handle_id = self.get_handle_id_by_name(telepathy.constants.HANDLE_TYPE_ROOM, str(conf_name)) - print 'room_handle_id:', room_handle_id + #print 'room_handle_id:', room_handle_id handles = [] diff --git a/sunshine/contacts.py b/sunshine/contacts.py index 6dd3d0a..4d4dadb 100644 --- a/sunshine/contacts.py +++ b/sunshine/contacts.py @@ -36,7 +36,8 @@ class SunshineContacts(telepathy.server.ConnectionInterfaceContacts): telepathy.CONNECTION_INTERFACE_SIMPLE_PRESENCE : 'presence', telepathy.CONNECTION_INTERFACE_ALIASING : 'alias', telepathy.CONNECTION_INTERFACE_AVATARS : 'token', - telepathy.CONNECTION_INTERFACE_CAPABILITIES : 'caps' + telepathy.CONNECTION_INTERFACE_CAPABILITIES : 'caps', + telepathy.CONNECTION_INTERFACE_CONTACT_CAPABILITIES : 'capabilities' } def __init__(self): @@ -52,10 +53,12 @@ class SunshineContacts(telepathy.server.ConnectionInterfaceContacts): out_signature='a{ua{sv}}', sender_keyword='sender') def GetContactAttributes(self, handles, interfaces, hold, sender): #InspectHandle already checks we're connected, the handles and handle type. + supported_interfaces = set() for interface in interfaces: - if interface not in self.attributes: - raise telepathy.errors.InvalidArgument( - 'Interface %s is not supported by GetContactAttributes' % (interface)) + if interface in self.attributes: + supported_interfaces.add(interface) + else: + logger.debug("Ignoring unsupported interface %s" % interface) handle_type = telepathy.HANDLE_TYPE_CONTACT ret = {} @@ -65,14 +68,16 @@ class SunshineContacts(telepathy.server.ConnectionInterfaceContacts): functions = { telepathy.CONNECTION : lambda x: zip(x, self.InspectHandles(handle_type, x)), - telepathy.CONNECTION_INTERFACE_SIMPLE_PRESENCE : + telepathy.CONNECTION_INTERFACE_SIMPLE_PRESENCE: lambda x: self.GetPresences(x).items(), - telepathy.CONNECTION_INTERFACE_ALIASING : + telepathy.CONNECTION_INTERFACE_ALIASING: lambda x: self.GetAliases(x).items(), - telepathy.CONNECTION_INTERFACE_AVATARS : + telepathy.CONNECTION_INTERFACE_AVATARS: lambda x: self.GetKnownAvatarTokens(x).items(), - telepathy.CONNECTION_INTERFACE_CAPABILITIES : - lambda x: self.GetCapabilities(x).items() + telepathy.CONNECTION_INTERFACE_CAPABILITIES: + lambda x: self.GetCapabilities(x).items(), + telepathy.CONNECTION_INTERFACE_CONTACT_CAPABILITIES: + lambda x: self.GetContactCapabilities(x).items() } #Hold handles if needed @@ -81,8 +86,9 @@ class SunshineContacts(telepathy.server.ConnectionInterfaceContacts): # Attributes from the interface org.freedesktop.Telepathy.Connection # are always returned, and need not be requested explicitly. - interfaces = set(interfaces + [telepathy.CONNECTION]) - for interface in interfaces: + supported_interfaces.add(telepathy.CONNECTION) + + for interface in supported_interfaces: interface_attribute = interface + '/' + self.attributes[interface] results = functions[interface](handles) for handle, value in results: |