summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sunshine/capabilities.py229
-rw-r--r--sunshine/connection.py16
-rw-r--r--sunshine/contacts.py28
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: