Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/presence/PresenceService.py124
-rw-r--r--services/presence/Service.py119
2 files changed, 182 insertions, 61 deletions
diff --git a/services/presence/PresenceService.py b/services/presence/PresenceService.py
index 1534ae9..c04084c 100644
--- a/services/presence/PresenceService.py
+++ b/services/presence/PresenceService.py
@@ -56,6 +56,38 @@ class ServiceAdv(object):
raise ValueError("Can't reset to resolve pending from resolved.")
self._state = state
+class RegisteredServiceType(object):
+ def __init__(self, stype):
+ self._stype = stype
+ self._refcount = 1
+
+ def get_type(self):
+ return self._stype
+
+ def ref(self):
+ self._refcount += 1
+
+ def unref(self):
+ self._refcount -= 1
+ return self._refcount
+
+def _txt_to_dict(txt):
+ """Convert an avahi-returned TXT record formatted
+ as nested arrays of integers (from dbus) into a dict
+ of key/value string pairs."""
+ prop_dict = {}
+ props = avahi.txt_array_to_string_array(txt)
+ for item in props:
+ key = value = None
+ if '=' not in item:
+ # No = means a boolean value of true
+ key = item
+ value = True
+ else:
+ (key, value) = item.split('=')
+ prop_dict[key] = value
+ return prop_dict
+
_PRESENCE_SERVICE = "org.laptop.Presence"
_PRESENCE_DBUS_INTERFACE = "org.laptop.Presence"
@@ -169,8 +201,9 @@ class PresenceServiceDBusHelper(dbus.service.Object):
return owner.object_path()
@dbus.service.method(_PRESENCE_DBUS_INTERFACE,
- in_signature="os", out_signature="o")
- def joinActivity(self, activity_op, stype):
+ in_signature="os", out_signature="o",
+ sender_keyword="sender")
+ def joinActivity(self, activity_op, stype, sender):
found_activity = None
acts = self._parent.get_activities()
for act in acts:
@@ -179,29 +212,34 @@ class PresenceServiceDBusHelper(dbus.service.Object):
break
if not found_activity:
raise NotFoundError("The activity %s was not found." % activity_op)
- return self._parent.join_activity(found_activity, stype)
+ return self._parent.join_activity(found_activity, stype, sender)
@dbus.service.method(_PRESENCE_DBUS_INTERFACE,
- in_signature="ssa{ss}sis", out_signature="o")
- def shareActivity(self, activity_id, stype, properties, address, port, domain):
+ in_signature="ssa{ss}sis", out_signature="o",
+ sender_keyword="sender")
+ def shareActivity(self, activity_id, stype, properties, address, port,
+ domain, sender=None):
if not len(address):
address = None
service = self._parent.share_activity(activity_id, stype, properties, address,
- port, domain)
+ port, domain, sender)
return service.object_path()
@dbus.service.method(_PRESENCE_DBUS_INTERFACE,
- in_signature="ssa{ss}sis", out_signature="o")
- def registerService(self, name, stype, properties, address, port, domain):
+ in_signature="ssa{ss}sis", out_signature="o",
+ sender_keyword="sender")
+ def registerService(self, name, stype, properties, address, port, domain,
+ sender=None):
if not len(address):
address = None
service = self._parent.register_service(name, stype, properties, address,
- port, domain)
+ port, domain, sender)
return service.object_path()
@dbus.service.method(_PRESENCE_DBUS_INTERFACE,
- in_signature="o", out_signature="")
- def unregisterService(self, service_op):
+ in_signature="o", out_signature="",
+ sender_keyword="sender")
+ def unregisterService(self, service_op, sender):
found_serv = None
services = self._parent.get_services()
for serv in services:
@@ -210,7 +248,7 @@ class PresenceServiceDBusHelper(dbus.service.Object):
break
if not found_serv:
raise NotFoundError("The activity %s was not found." % service_op)
- return self._parent.unregister_service(found_serv)
+ return self._parent.unregister_service(found_serv, sender)
@dbus.service.method(_PRESENCE_DBUS_INTERFACE,
in_signature="s", out_signature="")
@@ -424,9 +462,10 @@ class PresenceService(object):
key = (full_name, stype)
if not self._services.has_key(key):
objid = self._get_next_object_id()
+ props = _txt_to_dict(txt)
service = Service.Service(self._bus_name, objid, name=full_name,
stype=stype, domain=domain, address=address, port=port,
- properties=txt, source_address=address, local=adv.is_local())
+ properties=props, source_address=address, local=adv.is_local())
self._services[key] = service
else:
# Already tracking this service; likely we were the one that shared it
@@ -588,19 +627,22 @@ class PresenceService(object):
def _new_domain_cb_glue(self, interface, protocol, domain, flags=0):
gobject.idle_add(self._new_domain_cb, interface, protocol, domain, flags)
- def join_activity(self, activity, stype):
+ def join_activity(self, activity, stype, sender):
services = activity.get_services_of_type(stype)
if not len(services):
- raise NotFoundError("The service type %s was not present within the activity %s" % (stype, activity.object_path()))
+ raise NotFoundError("The service type %s was not present within " \
+ "the activity %s" % (stype, activity.object_path()))
act_service = services[0]
props = act_service.get_properties()
color = activity.get_color()
if color:
props['color'] = color
return self._share_activity(activity.get_id(), stype, properties,
- act_service.get_address(), act_service.get_port(), act_service.get_domain())
+ act_service.get_address(), act_service.get_port(),
+ act_service.get_domain(), sender)
- def share_activity(self, activity_id, stype, properties=None, address=None, port=-1, domain=u"local"):
+ def share_activity(self, activity_id, stype, properties=None, address=None,
+ port=-1, domain=u"local", sender=None):
"""Convenience function to share an activity with other buddies."""
if not util.validate_activity_id(activity_id):
raise ValueError("invalid activity id")
@@ -621,14 +663,19 @@ class PresenceService(object):
if color:
properties['color'] = color
- logging.debug('Share activity %s, type %s, address %s, port %d, properties %s' % (activity_id, stype, address, port, properties))
- return self.register_service(real_name, stype, properties, address, port, domain)
+ logging.debug('Share activity %s, type %s, address %s, port %d, " \
+ "properties %s' % (activity_id, stype, address, port,
+ properties))
+ return self.register_service(real_name, stype, properties, address,
+ port, domain, sender)
- def register_service(self, name, stype, properties={}, address=None, port=-1, domain=u"local"):
+ def register_service(self, name, stype, properties={}, address=None,
+ port=-1, domain=u"local", sender=None):
"""Register a new service, advertising it to other Buddies on the network."""
(actid, person_name) = Service.decompose_service_name(name)
if self.get_owner() and person_name != self.get_owner().get_name():
- raise RuntimeError("Tried to register a service that didn't have Owner nick as the service name!")
+ raise RuntimeError("Tried to register a service that didn't have" \
+ " Owner nick as the service name!")
if not domain or not len(domain):
domain = u"local"
if not port or port == -1:
@@ -647,12 +694,14 @@ class PresenceService(object):
objid = self._get_next_object_id()
service = Service.Service(self._bus_name, objid, name=name,
stype=stype, domain=domain, address=address, port=port,
- properties=properties, source_address=None, local=True)
+ properties=properties, source_address=None, local=True,
+ local_publisher=sender)
self._services[(name, stype)] = service
port = service.get_port()
logging.debug("PS: Will register service with name='%s', stype='%s'," \
- " domain='%s', address='%s', port=%d, info='%s'" % (name, stype, domain, address, port, info))
+ " domain='%s', address='%s', port=%d, info='%s'" % (name, stype,
+ domain, address, port, info))
group.AddService(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, 0, dbus.String(name),
dbus.String(stype), dbus.String(domain), dbus.String(""), # let Avahi figure the 'host' out
dbus.UInt16(port), info)
@@ -667,7 +716,10 @@ class PresenceService(object):
self.register_service_type(stype)
return service
- def unregister_service(self, service):
+ def unregister_service(self, service, sender=None):
+ local_publisher = service.get_local_publisher()
+ if sender is not None and local_publisher != sender:
+ raise ValueError("Service was not not registered by requesting process!")
group = service.get_avahi_entry_group()
if not group:
raise ValueError("Service was not a local service provided by this laptop!")
@@ -678,9 +730,16 @@ class PresenceService(object):
a certain mDNS service types."""
if type(stype) != type(u""):
raise ValueError("service type must be a unicode string.")
- if stype in self._registered_service_types:
- return
- self._registered_service_types.append(stype)
+
+ # If we've already registered it as a service type, ref it and return
+ for item in self._registered_service_types:
+ if item.get_type() == stype:
+ item.ref()
+ return
+
+ # Otherwise track this type now
+ obj = RegisteredServiceType(stype)
+ self._registered_service_types.append(obj)
# Find unresolved services that match the service type
# we're now interested in, and resolve them
@@ -698,8 +757,15 @@ class PresenceService(object):
"""Stop tracking a certain mDNS service."""
if type(stype) != type(u""):
raise ValueError("service type must be a unicode string.")
- if stype in self._registered_service_types:
- self._registered_service_types.remove(stype)
+ item = None
+ for item in self._registered_service_types:
+ if item.get_type() == stype:
+ break
+ # if it was found, unref it and possibly remove it
+ if item is not None:
+ if item.unref() <= 0:
+ self._registered_service_types.remove(item)
+ del item
def main():
diff --git a/services/presence/Service.py b/services/presence/Service.py
index 3c65d99..0c23600 100644
--- a/services/presence/Service.py
+++ b/services/presence/Service.py
@@ -5,23 +5,6 @@ from sugar import util
import dbus, dbus.service
import random
-def _txt_to_dict(txt):
- """Convert an avahi-returned TXT record formatted
- as nested arrays of integers (from dbus) into a dict
- of key/value string pairs."""
- prop_dict = {}
- props = avahi.txt_array_to_string_array(txt)
- for item in props:
- key = value = None
- if '=' not in item:
- # No = means a boolean value of true
- key = item
- value = True
- else:
- (key, value) = item.split('=')
- prop_dict[key] = value
- return prop_dict
-
def compose_service_name(name, activity_id):
if type(name) == type(""):
name = unicode(name)
@@ -67,6 +50,11 @@ class ServiceDBusHelper(dbus.service.Object):
self._object_path = object_path
dbus.service.Object.__init__(self, bus_name, self._object_path)
+ @dbus.service.signal(SERVICE_DBUS_INTERFACE,
+ signature="as")
+ def PublishedValueChanged(self, keylist):
+ pass
+
@dbus.service.method(SERVICE_DBUS_INTERFACE,
in_signature="", out_signature="a{sv}")
def getProperties(self):
@@ -90,14 +78,33 @@ class ServiceDBusHelper(dbus.service.Object):
return pary
@dbus.service.method(SERVICE_DBUS_INTERFACE,
- in_signature="s", out_signature="s")
+ in_signature="s")
def getPublishedValue(self, key):
"""Return the value belonging to the requested key from the
service's TXT records."""
- value = self._parent.get_one_property(key)
- if type(value) == type(True):
- value = str(value)
- return value
+ val = self._parent.get_one_property(key)
+ if not val:
+ raise KeyError("Value was not found.")
+ return val
+
+ @dbus.service.method(SERVICE_DBUS_INTERFACE,
+ in_signature="", out_signature="a{sv}")
+ def getPublishedValues(self):
+ pary = {}
+ props = self._parent.get_properties()
+ for key, value in props.items():
+ pary[key] = str(value)
+ return dbus.Dictionary(pary)
+
+ @dbus.service.method(SERVICE_DBUS_INTERFACE,
+ sender_keyword="sender")
+ def setPublishedValue(self, key, value, sender):
+ self._parent.set_property(key, value, sender)
+
+ @dbus.service.method(SERVICE_DBUS_INTERFACE,
+ in_signature="a{sv}", sender_keyword="sender")
+ def setPublishedValues(self, values, sender):
+ self._parent.set_properties(values, sender)
class Service(object):
@@ -105,7 +112,7 @@ class Service(object):
service as advertised on the network."""
def __init__(self, bus_name, object_id, name, stype, domain=u"local",
address=None, port=-1, properties=None, source_address=None,
- local=False):
+ local=False, local_publisher=None):
if not bus_name:
raise ValueError("DBus bus name must be valid")
if not object_id or type(object_id) != type(1):
@@ -137,7 +144,7 @@ class Service(object):
self._port = -1
self.set_port(port)
self._properties = {}
- self.set_properties(properties)
+ self._internal_set_properties(properties, first_time=True)
self._avahi_entry_group = None
self._local = local
@@ -159,13 +166,19 @@ class Service(object):
if self._properties.has_key(_ACTIVITY_ID_TAG):
prop_actid = self._properties[_ACTIVITY_ID_TAG]
if (prop_actid and not actid) or (prop_actid != actid):
- raise ValueError("ActivityID property specified, but the service names's activity ID didn't match it: %s, %s" % (prop_actid, actid))
+ raise ValueError("ActivityID property specified, but the " \
+ "service names's activity ID didn't match it: %s," \
+ " %s" % (prop_actid, actid))
self._activity_id = actid
if actid and not self._properties.has_key(_ACTIVITY_ID_TAG):
self._properties[_ACTIVITY_ID_TAG] = actid
self._owner = None
+ # ID of the D-Bus connection that published this service, if any.
+ # We only let the local publisher modify the service.
+ self._local_publisher = local_publisher
+
# register ourselves with dbus
self._object_id = object_id
self._object_path = SERVICE_DBUS_OBJECT_PATH + str(self._object_id)
@@ -182,6 +195,9 @@ class Service(object):
raise RuntimeError("Can only set a service's owner once")
self._owner = owner
+ def get_local_publisher(self):
+ return self._local_publisher
+
def is_local(self):
return self._local
@@ -207,19 +223,55 @@ class Service(object):
properties."""
return self._properties
- def set_properties(self, properties):
+ def set_property(self, key, value, sender=None):
+ """Set one service property"""
+ if sender is not None and self._local_publisher != sender:
+ raise ValueError("Service was not not registered by requesting process!")
+
+ if type(key) != type(u""):
+ raise ValueError("Key must be a unicode string.")
+ if type(value) != type(u"") or type(value) != type(True):
+ raise ValueError("Key must be a unicode string or a boolean.")
+
+ # Ignore setting the key to it's current value
+ if self._properties.has_key(key):
+ if self._properties[key] == value:
+ return
+
+ # Blank value means remove key
+ remove = False
+ if type(value) == type(u"") and len(value) == 0:
+ remove = True
+ if type(value) == type(False) and value == False:
+ remove = True
+
+ if remove:
+ # If the key wasn't present, return without error
+ if self._properties.has_key(key):
+ del self._properties[key]
+ else:
+ # Otherwise set it
+ if type(value) == type(True):
+ value = ""
+ self._properties[key] = value
+ self._dbus_helper.PublishedValueChanged(key)
+
+ def set_properties(self, properties, sender=None):
+ """Set all service properties in one call"""
+ if sender is not None and self._local_publisher != sender:
+ raise ValueError("Service was not not registered by requesting process!")
+ self._internal_set_properties(properties)
+
+ def _internal_set_properties(self, properties, first_time=False):
"""Set the service's properties from either an Avahi
TXT record (a list of lists of integers), or a
python dictionary."""
+ if type(properties) != type({}):
+ raise ValueError("Properties must be a dictionary.")
self._properties = {}
- props = {}
- if type(properties) == type([]):
- props = _txt_to_dict(properties)
- elif type(properties) == type({}):
- props = properties
# Set key/value pairs on internal property list
- for key, value in props.items():
+ for key, value in properties.items():
if len(key) == 0:
continue
tmp_key = key
@@ -230,6 +282,9 @@ class Service(object):
tmp_val = unicode(tmp_val)
self._properties[tmp_key] = tmp_val
+ if not first_time:
+ self._dbus_helper.PublishedValueChanged(self._properties.keys())
+
def get_type(self):
"""Return the service's service type."""
return self._stype