Commit bce05e18 authored by Dan Pascu's avatar Dan Pascu

Refactored PresencePublicationHandler in order to simplify it

parent 484cb39c
...@@ -27,8 +27,8 @@ from sipsimple.account import AccountManager, BonjourAccount ...@@ -27,8 +27,8 @@ from sipsimple.account import AccountManager, BonjourAccount
from sipsimple.account.bonjour import BonjourPresenceState from sipsimple.account.bonjour import BonjourPresenceState
from sipsimple.account.xcap import Icon, OfflineStatus from sipsimple.account.xcap import Icon, OfflineStatus
from sipsimple.configuration.settings import SIPSimpleSettings from sipsimple.configuration.settings import SIPSimpleSettings
from sipsimple.payloads import caps, cipid, pidf, prescontent, rpid from sipsimple.payloads import caps, pidf, prescontent, rpid
from sipsimple.threading import run_in_twisted_thread from sipsimple.payloads import cipid; cipid # needs to be imported to register its namespace and extensions
from sipsimple.threading.green import run_in_green_thread from sipsimple.threading.green import run_in_green_thread
from sipsimple.util import ISOTimestamp from sipsimple.util import ISOTimestamp
...@@ -43,145 +43,134 @@ sip_prefix_re = re.compile("^sips?:") ...@@ -43,145 +43,134 @@ sip_prefix_re = re.compile("^sips?:")
unknown_icon = "blink://unknown" unknown_icon = "blink://unknown"
class PresencePublicationHandler(object): class BlinkPresenceState(object):
implements(IObserver) def __init__(self, account):
self.account = account
def start(self): @property
notification_center = NotificationCenter() def online_state(self):
notification_center.add_observer(self, name='SIPAccountWillActivate') blink_settings = BlinkSettings()
notification_center.add_observer(self, name='SIPAccountWillDeactivate')
notification_center.add_observer(self, name='SIPAccountDidDiscoverXCAPSupport')
notification_center.add_observer(self, name='SystemDidWakeUpFromSleep')
notification_center.add_observer(self, name='XCAPManagerDidReloadData')
notification_center.add_observer(self, sender=BlinkSettings(), name='CFGSettingsObjectDidChange')
try:
self.hostname = socket.gethostname()
except Exception:
self.hostname = 'localhost'
def stop(self):
notification_center = NotificationCenter()
notification_center.remove_observer(self, name='SIPAccountWillActivate')
notification_center.remove_observer(self, name='SIPAccountWillDeactivate')
notification_center.remove_observer(self, name='SIPAccountDidDiscoverXCAPSupport')
notification_center.remove_observer(self, name='SystemDidWakeUpFromSleep')
notification_center.remove_observer(self, name='XCAPManagerDidReloadData')
notification_center.remove_observer(self, sender=BlinkSettings(), name='CFGSettingsObjectDidChange')
def publish(self, accounts): state = blink_settings.presence.current_state.state
bonjour_account = BonjourAccount() note = blink_settings.presence.current_state.note
for account in accounts:
if account is not bonjour_account:
account.presence_state = self.build_pidf(account)
else:
blink_settings = BlinkSettings()
account.presence_state = BonjourPresenceState(blink_settings.presence.current_state.state, blink_settings.presence.current_state.note)
def build_pidf(self, account): state = 'offline' if state=='Invisible' else state.lower()
blink_settings = BlinkSettings()
presence_state = blink_settings.presence.current_state.state
presence_note = blink_settings.presence.current_state.note
if presence_state == 'Invisible': if self.account is BonjourAccount():
# Publish an empty offline state so that other clients are also synced return BonjourPresenceState(state, note)
return self.build_offline_pidf(account, None)
doc = pidf.PIDF(str(account.uri)) try:
hostname = socket.gethostname()
except Exception:
hostname = 'localhost'
account_id = hashlib.md5(self.account.id).hexdigest()
timestamp = ISOTimestamp.now() timestamp = ISOTimestamp.now()
person = pidf.Person('PID-%s' % hashlib.md5(account.id).hexdigest()) doc = pidf.PIDF(str(self.account.uri))
person.timestamp = pidf.PersonTimestamp(timestamp)
doc.add(person)
status = pidf.Status(basic='open')
status.extended = presence_state.lower()
person = pidf.Person('PID-%s' % account_id)
person.timestamp = timestamp
person.activities = rpid.Activities() person.activities = rpid.Activities()
person.activities.add(unicode(status.extended)) person.activities.add(state)
doc.add(person)
settings = SIPSimpleSettings()
instance_id = str(uuid.UUID(settings.instance_id))
service = pidf.Service('SID-%s' % instance_id, status=status)
if presence_note:
service.notes.add(presence_note)
service.timestamp = pidf.ServiceTimestamp(timestamp)
service.contact = pidf.Contact(str(account.contact.public_gruu or account.uri))
if account.display_name:
service.display_name = cipid.DisplayName(account.display_name)
if account.xcap.icon:
service.icon = cipid.Icon("%s#blink-icon%s" % (account.xcap.icon.url, account.xcap.icon.etag))
else:
service.icon = cipid.Icon(unknown_icon)
service.device_info = pidf.DeviceInfo(instance_id, description=self.hostname, user_agent=settings.user_agent)
service.device_info.time_offset = pidf.TimeOffset()
service.capabilities = caps.ServiceCapabilities(audio=True, text=False)
service.capabilities.message = False
service.capabilities.file_transfer = False
service.capabilities.screen_sharing_server = False
service.capabilities.screen_sharing_client = False
# TODO: Add real user input data -Saul
service.user_input = rpid.UserInput()
service.user_input.idle_threshold = 600
service.add(pidf.DeviceID(instance_id))
doc.add(service)
device = pidf.Device('DID-%s' % instance_id, device_id=pidf.DeviceID(instance_id)) if state == 'offline':
device.timestamp = pidf.DeviceTimestamp(timestamp) service = pidf.Service('SID-%s' % account_id)
device.notes.add(u'%s at %s' % (settings.user_agent, self.hostname)) service.status = 'closed'
doc.add(device) service.status.extended = state
service.contact = str(self.account.uri)
service.timestamp = timestamp
service.capabilities = caps.ServiceCapabilities()
doc.add(service)
else:
settings = SIPSimpleSettings()
instance_id = str(uuid.UUID(settings.instance_id))
service = pidf.Service('SID-%s' % instance_id)
service.status = 'open'
service.status.extended = state
service.contact = str(self.account.contact.public_gruu or self.account.uri)
service.timestamp = timestamp
service.capabilities = caps.ServiceCapabilities()
service.capabilities.audio = True
service.capabilities.text = False
service.capabilities.message = False
service.capabilities.file_transfer = False
service.capabilities.screen_sharing_server = False
service.capabilities.screen_sharing_client = False
service.display_name = self.account.display_name or None
service.icon = "%s#blink-icon%s" % (self.account.xcap.icon.url, self.account.xcap.icon.etag) if self.account.xcap.icon else unknown_icon
service.device_info = pidf.DeviceInfo(instance_id, description=hostname, user_agent=settings.user_agent)
service.device_info.time_offset = pidf.TimeOffset()
# TODO: Add real user input data -Saul
service.user_input = rpid.UserInput()
service.user_input.idle_threshold = 600
service.add(pidf.DeviceID(instance_id))
if note:
service.notes.add(note)
doc.add(service)
device = pidf.Device('DID-%s' % instance_id, device_id=pidf.DeviceID(instance_id))
device.timestamp = timestamp
device.notes.add(u'%s at %s' % (settings.user_agent, hostname))
doc.add(device)
return doc return doc
def build_offline_pidf(self, account, note=None): @property
doc = pidf.PIDF(str(account.uri)) def offline_state(self):
blink_settings = BlinkSettings()
if self.account is BonjourAccount() or not blink_settings.presence.offline_note:
return None
account_id = hashlib.md5(self.account.id).hexdigest()
timestamp = ISOTimestamp.now() timestamp = ISOTimestamp.now()
account_hash = hashlib.md5(account.id).hexdigest() doc = pidf.PIDF(str(self.account.uri))
person = pidf.Person('PID-%s' % account_hash)
person.timestamp = pidf.PersonTimestamp(timestamp)
doc.add(person)
person = pidf.Person('PID-%s' % account_id)
person.timestamp = timestamp
person.activities = rpid.Activities() person.activities = rpid.Activities()
person.activities.add(u'offline') person.activities.add('offline')
doc.add(person)
service = pidf.Service('SID-%s' % account_hash) service = pidf.Service('SID-%s' % account_id)
service.status = pidf.Status(basic='closed') service.status = 'closed'
service.status.extended = u'offline' service.status.extended = 'offline'
service.contact = pidf.Contact(str(account.uri)) service.contact = str(self.account.uri)
service.timestamp = timestamp
service.capabilities = caps.ServiceCapabilities() service.capabilities = caps.ServiceCapabilities()
service.timestamp = pidf.ServiceTimestamp(timestamp) service.notes.add(blink_settings.presence.offline_note)
if note:
service.notes.add(note)
doc.add(service) doc.add(service)
return doc return doc
def set_xcap_offline_note(self, accounts):
blink_settings = BlinkSettings()
for account in accounts:
status = OfflineStatus(self.build_offline_pidf(account, blink_settings.presence.offline_note)) if blink_settings.presence.offline_note else None
account.xcap_manager.set_offline_status(status)
def set_xcap_icon(self, accounts): class PresencePublicationHandler(object):
icon_manager = IconManager() implements(IObserver)
icon = Icon(icon_manager.get_image('avatar'), 'image/png')
for account in accounts:
account.xcap_manager.set_status_icon(icon)
@run_in_gui_thread def start(self):
def _save_icon(self, icon_data, icon_hash): notification_center = NotificationCenter()
blink_settings = BlinkSettings() notification_center.add_observer(self, name='SIPAccountWillActivate')
icon_manager = IconManager() notification_center.add_observer(self, name='SIPAccountWillDeactivate')
if icon_data is not None is not icon_hash: notification_center.add_observer(self, name='SIPAccountDidDiscoverXCAPSupport')
icon = icon_manager.store_data('avatar', icon_data) notification_center.add_observer(self, name='SystemDidWakeUpFromSleep')
blink_settings.presence.icon = IconDescriptor('file://' + icon.filename, icon_hash) if icon is not None else None notification_center.add_observer(self, name='XCAPManagerDidReloadData')
else: notification_center.add_observer(self, sender=BlinkSettings(), name='CFGSettingsObjectDidChange')
icon_manager.remove('avatar') try:
blink_settings.presence.icon = None self.hostname = socket.gethostname()
blink_settings.save() except Exception:
self.hostname = 'localhost'
def stop(self):
notification_center = NotificationCenter()
notification_center.remove_observer(self, name='SIPAccountWillActivate')
notification_center.remove_observer(self, name='SIPAccountWillDeactivate')
notification_center.remove_observer(self, name='SIPAccountDidDiscoverXCAPSupport')
notification_center.remove_observer(self, name='SystemDidWakeUpFromSleep')
notification_center.remove_observer(self, name='XCAPManagerDidReloadData')
notification_center.remove_observer(self, sender=BlinkSettings(), name='CFGSettingsObjectDidChange')
@run_in_twisted_thread
def handle_notification(self, notification): def handle_notification(self, notification):
handler = getattr(self, '_NH_%s' % notification.name, Null) handler = getattr(self, '_NH_%s' % notification.name, Null)
handler(notification) handler(notification)
...@@ -189,36 +178,36 @@ class PresencePublicationHandler(object): ...@@ -189,36 +178,36 @@ class PresencePublicationHandler(object):
def _NH_CFGSettingsObjectDidChange(self, notification): def _NH_CFGSettingsObjectDidChange(self, notification):
if notification.sender is BlinkSettings(): if notification.sender is BlinkSettings():
account_manager = AccountManager() account_manager = AccountManager()
if set(['presence.icon', 'presence.offline_note']).intersection(notification.data.modified): if 'presence.offline_note' in notification.data.modified:
# TODO: use a transaction here as well? -Dan for account in (account for account in account_manager.get_accounts() if hasattr(account, 'xcap') and account.enabled and account.xcap.enabled and account.xcap.discovered):
accounts = [account for account in account_manager.get_accounts() if hasattr(account, 'xcap') and account.enabled and account.xcap.enabled and account.xcap.discovered] state = BlinkPresenceState(account).offline_state
if 'presence.offline_note' in notification.data.modified: account.xcap_manager.set_offline_status(OfflineStatus(state) if state is not None else None)
self.set_xcap_offline_note(accounts) if 'presence.icon' in notification.data.modified:
if 'presence.icon' in notification.data.modified: icon_data = IconManager().get_image('avatar')
self.set_xcap_icon(accounts) icon = Icon(icon_data, 'image/png') if icon_data is not None else None
for account in (account for account in account_manager.get_accounts() if hasattr(account, 'xcap') and account.enabled and account.xcap.enabled and account.xcap.discovered):
account.xcap_manager.set_status_icon(icon)
if 'presence.current_state' in notification.data.modified: if 'presence.current_state' in notification.data.modified:
accounts = [account for account in account_manager.get_accounts() if account.enabled and account.presence.enabled] for account in (account for account in account_manager.get_accounts() if account.enabled and account.presence.enabled):
self.publish(accounts) account.presence_state = BlinkPresenceState(account).online_state
else: else:
account = notification.sender account = notification.sender
if set(['xcap.enabled', 'xcap.xcap_root']).intersection(notification.data.modified): if set(['xcap.enabled', 'xcap.xcap_root']).intersection(notification.data.modified):
account.xcap.icon = None account.xcap.icon = None
account.save() account.save()
if set(['presence.enabled', 'display_name', 'xcap.enabled', 'xcap.icon', 'xcap.xcap_root']).intersection(notification.data.modified) and account.presence.enabled: if set(['presence.enabled', 'display_name', 'xcap.enabled', 'xcap.icon', 'xcap.xcap_root']).intersection(notification.data.modified) and account.presence.enabled:
self.publish([account]) account.presence_state = BlinkPresenceState(account).online_state
def _NH_SIPAccountWillActivate(self, notification): def _NH_SIPAccountWillActivate(self, notification):
account = notification.sender account = notification.sender
notification.center.add_observer(self, sender=account, name='CFGSettingsObjectDidChange') notification.center.add_observer(self, sender=account, name='CFGSettingsObjectDidChange')
if account is not BonjourAccount(): notification.center.add_observer(self, sender=account, name='SIPAccountGotSelfPresenceState')
notification.center.add_observer(self, sender=account, name='SIPAccountGotSelfPresenceState') account.presence_state = BlinkPresenceState(account).online_state
self.publish([account])
def _NH_SIPAccountWillDeactivate(self, notification): def _NH_SIPAccountWillDeactivate(self, notification):
account = notification.sender account = notification.sender
notification.center.remove_observer(self, sender=account, name='CFGSettingsObjectDidChange') notification.center.remove_observer(self, sender=account, name='CFGSettingsObjectDidChange')
if account is not BonjourAccount(): notification.center.remove_observer(self, sender=account, name='SIPAccountGotSelfPresenceState')
notification.center.remove_observer(self, sender=account, name='SIPAccountGotSelfPresenceState')
def _NH_SIPAccountGotSelfPresenceState(self, notification): def _NH_SIPAccountGotSelfPresenceState(self, notification):
pidf_doc = notification.data.pidf pidf_doc = notification.data.pidf
...@@ -250,12 +239,16 @@ class PresencePublicationHandler(object): ...@@ -250,12 +239,16 @@ class PresencePublicationHandler(object):
def _NH_SIPAccountDidDiscoverXCAPSupport(self, notification): def _NH_SIPAccountDidDiscoverXCAPSupport(self, notification):
account = notification.sender account = notification.sender
with account.xcap_manager.transaction(): with account.xcap_manager.transaction():
self.set_xcap_offline_note([account]) state = BlinkPresenceState(account).offline_state
self.set_xcap_icon([account]) icon_data = IconManager().get_image('avatar')
account.xcap_manager.set_offline_status(OfflineStatus(state) if state is not None else None)
account.xcap_manager.set_status_icon(Icon(icon_data, 'image/png') if icon_data is not None else None)
@run_in_gui_thread
def _NH_XCAPManagerDidReloadData(self, notification): def _NH_XCAPManagerDidReloadData(self, notification):
account = notification.sender.account account = notification.sender.account
blink_settings = BlinkSettings() blink_settings = BlinkSettings()
icon_manager = IconManager()
offline_status = notification.data.offline_status offline_status = notification.data.offline_status
status_icon = notification.data.status_icon status_icon = notification.data.status_icon
...@@ -271,14 +264,15 @@ class PresencePublicationHandler(object): ...@@ -271,14 +264,15 @@ class PresencePublicationHandler(object):
if status_icon: if status_icon:
icon_desc = IconDescriptor(notification.sender.status_icon.uri, notification.sender.status_icon.etag) icon_desc = IconDescriptor(notification.sender.status_icon.uri, notification.sender.status_icon.etag)
icon_hash = hashlib.sha512(status_icon.data).hexdigest() icon_hash = hashlib.sha512(status_icon.data).hexdigest()
if blink_settings.presence.icon and blink_settings.presence.icon.etag == icon_hash: if not blink_settings.presence.icon or blink_settings.presence.icon.etag != icon_hash:
# Icon didn't change icon = icon_manager.store_data('avatar', status_icon.data)
pass blink_settings.presence.icon = IconDescriptor('file://' + icon.filename, icon_hash) if icon is not None else None
else: blink_settings.save()
self._save_icon(status_icon.data, icon_hash)
else: else:
icon_desc = None icon_desc = None
self._save_icon(None, None) icon_manager.remove('avatar')
blink_settings.presence.icon = None
blink_settings.save()
account.xcap.icon = icon_desc account.xcap.icon = icon_desc
account.save() account.save()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment