Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/services/presence/buddy.py
diff options
context:
space:
mode:
Diffstat (limited to 'services/presence/buddy.py')
-rw-r--r--services/presence/buddy.py638
1 files changed, 0 insertions, 638 deletions
diff --git a/services/presence/buddy.py b/services/presence/buddy.py
deleted file mode 100644
index 82a9b44..0000000
--- a/services/presence/buddy.py
+++ /dev/null
@@ -1,638 +0,0 @@
-"""An "actor" on the network, whether remote or local"""
-# Copyright (C) 2007, Red Hat, Inc.
-# Copyright (C) 2007, Collabora Ltd.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-import os
-import gobject
-import dbus
-import dbus.service
-from dbus.gobject_service import ExportedGObject
-import psutils
-
-from sugar import env, profile
-import logging
-
-_BUDDY_PATH = "/org/laptop/Sugar/Presence/Buddies/"
-_BUDDY_INTERFACE = "org.laptop.Sugar.Presence.Buddy"
-_OWNER_INTERFACE = "org.laptop.Sugar.Presence.Buddy.Owner"
-
-_PROP_NICK = "nick"
-_PROP_KEY = "key"
-_PROP_ICON = "icon"
-_PROP_CURACT = "current-activity"
-_PROP_COLOR = "color"
-_PROP_OWNER = "owner"
-_PROP_VALID = "valid"
-_PROP_OBJID = 'objid'
-
-# Will go away soon
-_PROP_IP4_ADDRESS = "ip4-address"
-
-_logger = logging.getLogger('s-p-s.buddy')
-
-
-class Buddy(ExportedGObject):
- """Person on the network (tracks properties and shared activites)
-
- The Buddy is a collection of metadata describing a particular
- actor/person on the network. The Buddy object tracks a set of
- activities which the actor has shared with the presence service.
-
- Buddies have a "valid" property which is used to flag Buddies
- which are no longer reachable. That is, a Buddy may represent
- a no-longer reachable target on the network.
-
- The Buddy emits GObject events that the PresenceService uses
- to track changes in its status.
-
- Attributes:
-
- _activities -- dictionary mapping activity ID to
- activity.Activity objects
- handles -- dictionary mapping Telepathy client plugin to
- contact handle (an integer representing the JID or unique ID);
- channel-specific handles do not appear here
- """
-
- __gsignals__ = {
- 'validity-changed':
- # The buddy's validity changed.
- # Validity starts off False, and becomes True when the buddy
- # has a color, a nick and a key.
- # * the new validity: bool
- (gobject.SIGNAL_RUN_FIRST, None, [bool]),
- 'property-changed':
- # One of the buddy's properties has changed.
- # * those properties that have changed:
- # dict { str => object }
- (gobject.SIGNAL_RUN_FIRST, None, [object]),
- 'icon-changed':
- # The buddy's icon changed.
- # * the bytes of the icon: str
- (gobject.SIGNAL_RUN_FIRST, None, [object]),
- 'disappeared':
- # The buddy is offline (has no Telepathy handles and is not the
- # Owner)
- (gobject.SIGNAL_RUN_FIRST, None, []),
- }
-
- __gproperties__ = {
- _PROP_KEY : (str, None, None, None, gobject.PARAM_READWRITE),
- _PROP_ICON : (object, None, None, gobject.PARAM_READWRITE),
- _PROP_NICK : (str, None, None, None, gobject.PARAM_READWRITE),
- _PROP_COLOR : (str, None, None, None, gobject.PARAM_READWRITE),
- _PROP_CURACT : (str, None, None, None, gobject.PARAM_READWRITE),
- _PROP_VALID : (bool, None, None, False, gobject.PARAM_READABLE),
- _PROP_OWNER : (bool, None, None, False, gobject.PARAM_READABLE),
- _PROP_OBJID : (str, None, None, None, gobject.PARAM_READABLE),
- _PROP_IP4_ADDRESS : (str, None, None, None, gobject.PARAM_READWRITE)
- }
-
- def __init__(self, bus, object_id, **kwargs):
- """Initialize the Buddy object
-
- bus -- connection to the D-Bus session bus
- object_id -- the buddy's unique identifier, either based on their
- key-ID or JID
- kwargs -- used to initialize the object's properties
-
- constructs a DBUS "object path" from the _BUDDY_PATH
- and object_id
- """
-
- self._object_id = object_id
- self._object_path = dbus.ObjectPath(_BUDDY_PATH + object_id)
-
- self._activities = {} # Activity ID -> Activity
- self._activity_sigids = {}
- self.handles = {} # tp client -> handle
-
- self._valid = False
- self._owner = False
- self._key = None
- self._icon = ''
- self._current_activity = None
- self._nick = None
- self._color = None
- self._ip4_address = None
-
- _ALLOWED_INIT_PROPS = [_PROP_NICK, _PROP_KEY, _PROP_ICON,
- _PROP_CURACT, _PROP_COLOR, _PROP_IP4_ADDRESS]
- for (key, value) in kwargs.items():
- if key not in _ALLOWED_INIT_PROPS:
- _logger.debug("Invalid init property '%s'; ignoring..." % key)
- del kwargs[key]
-
- # Set icon after superclass init, because it sends DBus and GObject
- # signals when set
- icon_data = None
- if kwargs.has_key(_PROP_ICON):
- icon_data = kwargs[_PROP_ICON]
- del kwargs[_PROP_ICON]
-
- ExportedGObject.__init__(self, bus, self._object_path,
- gobject_properties=kwargs)
-
- if icon_data:
- self.props.icon = icon_data
-
- def do_get_property(self, pspec):
- """Retrieve current value for the given property specifier
-
- pspec -- property specifier with a "name" attribute
- """
- if pspec.name == _PROP_OBJID:
- return self._object_id
- elif pspec.name == _PROP_KEY:
- return self._key
- elif pspec.name == _PROP_ICON:
- return self._icon
- elif pspec.name == _PROP_NICK:
- return self._nick
- elif pspec.name == _PROP_COLOR:
- return self._color
- elif pspec.name == _PROP_CURACT:
- if not self._current_activity:
- return None
- if not self._activities.has_key(self._current_activity):
- return None
- return self._current_activity
- elif pspec.name == _PROP_VALID:
- return self._valid
- elif pspec.name == _PROP_OWNER:
- return self._owner
- elif pspec.name == _PROP_IP4_ADDRESS:
- return self._ip4_address
-
- def do_set_property(self, pspec, value):
- """Set given property
-
- pspec -- property specifier with a "name" attribute
- value -- value to set
-
- emits 'icon-changed' signal on icon setting
- calls _update_validity on all calls
- """
- if pspec.name == _PROP_ICON:
- if str(value) != self._icon:
- self._icon = str(value)
- self.IconChanged(self._icon)
- self.emit('icon-changed', self._icon)
- elif pspec.name == _PROP_NICK:
- self._nick = value
- elif pspec.name == _PROP_COLOR:
- self._color = value
- elif pspec.name == _PROP_CURACT:
- self._current_activity = value
- elif pspec.name == _PROP_KEY:
- if self._key:
- raise RuntimeError("Key already set.")
- self._key = value
- elif pspec.name == _PROP_IP4_ADDRESS:
- self._ip4_address = value
-
- self._update_validity()
-
- # dbus signals
- @dbus.service.signal(_BUDDY_INTERFACE,
- signature="ay")
- def IconChanged(self, icon_data):
- """Generates DBUS signal with icon_data"""
-
- @dbus.service.signal(_BUDDY_INTERFACE,
- signature="o")
- def JoinedActivity(self, activity_path):
- """Generates DBUS signal when buddy joins activity
-
- activity_path -- DBUS path to the activity object
- """
-
- @dbus.service.signal(_BUDDY_INTERFACE,
- signature="o")
- def LeftActivity(self, activity_path):
- """Generates DBUS signal when buddy leaves activity
-
- activity_path -- DBUS path to the activity object
- """
-
- @dbus.service.signal(_BUDDY_INTERFACE,
- signature="a{sv}")
- def PropertyChanged(self, updated):
- """Generates DBUS signal when buddy's property changes
-
- updated -- updated property-set (dictionary) with the
- Buddy's property (changed) values. Note: not the
- full set of properties, just the changes.
- """
-
- def add_telepathy_handle(self, tp_client, handle):
- """Add a Telepathy handle."""
- conn = tp_client.get_connection()
- self.TelepathyHandleAdded(conn.service_name, conn.object_path, handle)
- self.handles[tp_client] = handle
-
- @dbus.service.signal(_BUDDY_INTERFACE, signature='sou')
- def TelepathyHandleAdded(self, tp_conn_name, tp_conn_path, handle):
- """Another Telepathy handle has become associated with the buddy.
-
- This must only be emitted for non-channel-specific handles.
-
- tp_conn_name -- The bus name at which the Telepathy connection may be
- found
- tp_conn_path -- The object path at which the Telepathy connection may
- be found
- handle -- The handle of type CONTACT, which is not channel-specific,
- newly associated with the buddy
- """
-
- def remove_telepathy_handle(self, tp_client, handle):
- """Remove a Telepathy handle."""
- conn = tp_client.get_connection()
- my_handle = self.handles.get(tp_client, 0)
- if my_handle == handle:
- del self.handles[tp_client]
- self.TelepathyHandleRemoved(conn.service_name, conn.object_path,
- handle)
- # the Owner can't disappear - that would be silly
- if not self.handles and not self._owner:
- self.emit('disappeared')
- else:
- _logger.debug('Telepathy handle %u supposedly removed, but '
- 'my handle on that connection is %u - ignoring',
- handle, my_handle)
-
- @dbus.service.signal(_BUDDY_INTERFACE, signature='sou')
- def TelepathyHandleRemoved(self, tp_conn_name, tp_conn_path, handle):
- """A Telepathy handle has ceased to be associated with the buddy,
- probably because that contact went offline.
-
- The parameters are the same as for TelepathyHandleAdded.
- """
-
- # dbus methods
- @dbus.service.method(_BUDDY_INTERFACE,
- in_signature="", out_signature="ay")
- def GetIcon(self):
- """Retrieve Buddy's icon data
-
- returns empty string or dbus.ByteArray
- """
- if not self.props.icon:
- return ""
- return dbus.ByteArray(self.props.icon)
-
- @dbus.service.method(_BUDDY_INTERFACE,
- in_signature="", out_signature="ao")
- def GetJoinedActivities(self):
- """Retrieve set of Buddy's joined activities (paths)
-
- returns list of dbus service paths for the Buddy's joined
- activities
- """
- acts = []
- for act in self.get_joined_activities():
- if act.props.valid:
- acts.append(act.object_path())
- return acts
-
- @dbus.service.method(_BUDDY_INTERFACE,
- in_signature="", out_signature="a{sv}")
- def GetProperties(self):
- """Retrieve set of Buddy's properties
-
- returns dictionary of
- nick : str(nickname)
- owner : bool( whether this Buddy is an owner??? )
- XXX what is the owner flag for?
- key : str(public-key)
- color: Buddy's icon colour
- XXX what type?
- current-activity: Buddy's current activity_id, or
- "" if no current activity
- """
- props = {}
- props[_PROP_NICK] = self.props.nick
- props[_PROP_OWNER] = self.props.owner
- props[_PROP_KEY] = self.props.key
- props[_PROP_COLOR] = self.props.color
-
- if self.props.ip4_address:
- props[_PROP_IP4_ADDRESS] = self.props.ip4_address
- else:
- props[_PROP_IP4_ADDRESS] = ""
-
- if self.props.current_activity:
- props[_PROP_CURACT] = self.props.current_activity
- else:
- props[_PROP_CURACT] = ""
- return props
-
- @dbus.service.method(_BUDDY_INTERFACE,
- in_signature='', out_signature='a(sou)')
- def GetTelepathyHandles(self):
- """Return a list of non-channel-specific Telepathy contact handles
- associated with this Buddy.
-
- :Returns:
- An array of triples (connection well-known bus name, connection
- object path, handle).
- """
- ret = []
- for plugin in self.handles:
- conn = plugin.get_connection()
- ret.append((str(conn.service_name), conn.object_path,
- self.handles[plugin]))
-
- # methods
- def object_path(self):
- """Retrieve our dbus.ObjectPath object"""
- return dbus.ObjectPath(self._object_path)
-
- def _activity_validity_changed_cb(self, activity, valid):
- """Join or leave the activity when its validity changes"""
- if valid:
- self.JoinedActivity(activity.object_path())
- else:
- self.LeftActivity(activity.object_path())
-
- def add_activity(self, activity):
- """Add an activity to the Buddy's set of activities
-
- activity -- activity.Activity instance
-
- calls JoinedActivity
- """
- actid = activity.props.id
- if self._activities.has_key(actid):
- return
- self._activities[actid] = activity
- # join/leave activity when it's validity changes
- sigid = activity.connect("validity-changed",
- self._activity_validity_changed_cb)
- self._activity_sigids[actid] = sigid
- if activity.props.valid:
- self.JoinedActivity(activity.object_path())
-
- def remove_activity(self, activity):
- """Remove the activity from the Buddy's set of activities
-
- activity -- activity.Activity instance
-
- calls LeftActivity
- """
- actid = activity.props.id
- if not self._activities.has_key(actid):
- return
- activity.disconnect(self._activity_sigids[actid])
- del self._activity_sigids[actid]
- del self._activities[actid]
- if activity.props.valid:
- self.LeftActivity(activity.object_path())
-
- def get_joined_activities(self):
- """Retrieves list of still-valid activity objects"""
- acts = []
- for act in self._activities.values():
- acts.append(act)
- return acts
-
- def set_properties(self, properties):
- """Set the given set of properties on the object
-
- properties -- set of property values to set
-
- if no change, no events generated
- if change, generates property-changed and
- calls _update_validity
- """
- changed = False
- changed_props = {}
- if _PROP_NICK in properties:
- nick = properties[_PROP_NICK]
- if nick != self._nick:
- self._nick = nick
- changed_props[_PROP_NICK] = nick
- changed = True
- if _PROP_COLOR in properties:
- color = properties[_PROP_COLOR]
- if color != self._color:
- self._color = color
- changed_props[_PROP_COLOR] = color
- changed = True
- if _PROP_CURACT in properties:
- curact = properties[_PROP_CURACT]
- if curact != self._current_activity:
- self._current_activity = curact
- changed_props[_PROP_CURACT] = curact
- changed = True
- if _PROP_IP4_ADDRESS in properties:
- ip4addr = properties[_PROP_IP4_ADDRESS]
- if ip4addr != self._ip4_address:
- self._ip4_address = ip4addr
- changed_props[_PROP_IP4_ADDRESS] = ip4addr
- changed = True
- if _PROP_KEY in properties:
- # don't allow key to be set more than once
- if self._key is None:
- key = properties[_PROP_KEY]
- if key is not None:
- self._key = key
- changed_props[_PROP_KEY] = key
- changed = True
-
- if not changed or not changed_props:
- return
-
- # Try emitting PropertyChanged before updating validity
- # to avoid leaking a PropertyChanged signal before the buddy is
- # actually valid the first time after creation
- if self._valid:
- dbus_changed = {}
- for key, value in changed_props.items():
- if value:
- dbus_changed[key] = value
- else:
- dbus_changed[key] = ""
- self.PropertyChanged(dbus_changed)
-
- self.emit('property-changed', changed_props)
-
- self._update_validity()
-
- def _update_validity(self):
- """Check whether we are now valid
-
- validity is True if color, nick and key are non-null
-
- emits validity-changed if we have changed validity
- """
- try:
- old_valid = self._valid
- if self._color and self._nick and self._key:
- self._valid = True
- else:
- self._valid = False
-
- if old_valid != self._valid:
- self.emit("validity-changed", self._valid)
- except AttributeError:
- self._valid = False
-
-
-class GenericOwner(Buddy):
- """Common functionality for Local User-like objects
-
- The TestOwner wants to produce something *like* a
- ShellOwner, but with randomised changes and the like.
- This class provides the common features for a real
- local owner and a testing one.
- """
- __gtype_name__ = "GenericOwner"
-
- def __init__(self, ps, bus, object_id, **kwargs):
- """Initialize the GenericOwner instance
-
- ps -- presenceservice.PresenceService object
- bus -- a connection to the D-Bus session bus
- object_id -- the activity's unique identifier
- kwargs -- used to initialize the object's properties
-
- calls Buddy.__init__
- """
- self._ps = ps
- self._server = kwargs.pop("server", "olpc.collabora.co.uk")
- self._key_hash = kwargs.pop("key_hash", None)
- self._registered = kwargs.pop("registered", False)
-
- self._ip4_addr_monitor = psutils.IP4AddressMonitor.get_instance()
- self._ip4_addr_monitor.connect("address-changed",
- self._ip4_address_changed_cb)
- if self._ip4_addr_monitor.props.address:
- kwargs["ip4-address"] = self._ip4_addr_monitor.props.address
-
- Buddy.__init__(self, bus, object_id, **kwargs)
- self._owner = True
-
- self._bus = dbus.SessionBus()
-
- def _ip4_address_changed_cb(self, monitor, address):
- """Handle IPv4 address change, set property to generate event"""
- props = {_PROP_IP4_ADDRESS: address}
- self.set_properties(props)
-
- def get_registered(self):
- """Retrieve whether owner has registered with presence server"""
- return self._registered
-
- def get_server(self):
- """Retrieve XMPP server hostname (used by the server plugin)"""
- return self._server
-
- def get_key_hash(self):
- """Retrieve the user's private-key hash (used by the server plugin
- as a password)
- """
- return self._key_hash
-
- def set_registered(self, registered):
- """Customisation point: handle the registration of the owner"""
- raise RuntimeError("Subclasses must implement")
-
-
-class ShellOwner(GenericOwner):
- """Representation of the local-machine owner using Sugar's Shell
-
- The ShellOwner uses the Sugar Shell's dbus services to
- register for updates about the user's profile description.
- """
- __gtype_name__ = "ShellOwner"
-
- _SHELL_SERVICE = "org.laptop.Shell"
- _SHELL_OWNER_INTERFACE = "org.laptop.Shell.Owner"
- _SHELL_PATH = "/org/laptop/Shell"
-
- def __init__(self, ps, bus):
- """Initialize the ShellOwner instance
-
- ps -- presenceservice.PresenceService object
- bus -- a connection to the D-Bus session bus
-
- Retrieves initial property values from the profile
- module. Loads the buddy icon from file as well.
- XXX note: no error handling on that
-
- calls GenericOwner.__init__
- """
- server = profile.get_server()
- key_hash = profile.get_private_key_hash()
- registered = profile.get_server_registered()
- key = profile.get_pubkey()
- nick = profile.get_nick_name()
- color = profile.get_color().to_string()
-
- icon_file = os.path.join(env.get_profile_path(), "buddy-icon.jpg")
- f = open(icon_file, "r")
- icon = f.read()
- f.close()
-
- GenericOwner.__init__(self, ps, bus,
- 'keyid/' + psutils.pubkey_to_keyid(key),
- key=key, nick=nick, color=color, icon=icon, server=server,
- key_hash=key_hash, registered=registered)
-
- # Ask to get notifications on Owner object property changes in the
- # shell. If it's not currently running, no problem - we'll get the
- # signals when it does run
- for (signal, cb) in (('IconChanged', self._icon_changed_cb),
- ('ColorChanged', self._color_changed_cb),
- ('NickChanged', self._nick_changed_cb)):
- self._bus.add_signal_receiver(cb, signal_name=signal,
- dbus_interface=self._SHELL_OWNER_INTERFACE,
- bus_name=self._SHELL_SERVICE,
- path=self._SHELL_PATH)
-
- def set_registered(self, value):
- """Handle notification that we have been registered"""
- if value:
- profile.set_server_registered()
-
- def _icon_changed_cb(self, icon):
- """Handle icon change, set property to generate event"""
- self.props.icon = icon
-
- def _color_changed_cb(self, color):
- """Handle color change, set property to generate event"""
- props = {_PROP_COLOR: color}
- self.set_properties(props)
-
- def _nick_changed_cb(self, nick):
- """Handle nickname change, set property to generate event"""
- props = {_PROP_NICK: nick}
- self.set_properties(props)
-
- def _cur_activity_changed_cb(self, activity_id):
- """Handle current-activity change, set property to generate event
-
- Filters out local activities (those not in self.activites)
- because the network users can't join those activities, so
- the activity_id shared will be None in those cases...
- """
- if not self._activities.has_key(activity_id):
- # This activity is local-only
- activity_id = None
- props = {_PROP_CURACT: activity_id}
- self.set_properties(props)