- Clarify when a service type is supposed to be full/network, and when it's supposed

to be a short one.  Activities should _never_ be exposed to the full/network stype
    since that's an implementation detail of the PresenceService
- Make everything having to do with service objects non-unicode (?) to stop the madness.
    Ideally we want everything to be UTF-8 eventually.
- Fix up PS to deal with service types of short/long variety
- Remove a hack from the Start Page that gets all service advertisements, will fix soon
This commit is contained in:
Dan Williams 2006-06-16 15:27:50 -04:00
parent 38565321d5
commit b31a2176ba
3 changed files with 102 additions and 82 deletions

View File

@ -69,9 +69,7 @@ class PresenceService(gobject.GObject):
'buddy-disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
'activity-announced': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])),
'new-service-adv': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_STRING, gobject.TYPE_STRING]))
([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT]))
}
__lock = threading.Lock()
@ -106,7 +104,8 @@ class PresenceService(gobject.GObject):
self._activity_services = {}
# All the mdns service types we care about
self._allowed_service_types = []
self._allowed_service_types = [] # Short service type
self._allowed_activities = [] # activity UID
# Keep track of stuff we're already browsing with ZC
self._service_type_browsers = {}
@ -115,9 +114,6 @@ class PresenceService(gobject.GObject):
# Resolved service list
self._service_advs = []
# Main activity UID to filter services on
self._activity_uids = []
self._bus = dbus.SystemBus()
self._server = dbus.Interface(self._bus.get_object(avahi.DBUS_NAME,
avahi.DBUS_PATH_SERVER), avahi.DBUS_INTERFACE_SERVER)
@ -213,7 +209,7 @@ class PresenceService(gobject.GObject):
def _handle_new_service_for_activity(self, service, buddy):
# If the serivce is a group service, merge it into our groups list
(uid, ignore) = service.get_activity_uid()
uid = service.get_activity_uid()
if not uid:
uid = "*"
if not self._activity_services.has_key(uid):
@ -222,7 +218,7 @@ class PresenceService(gobject.GObject):
self.emit('activity-announced', service, buddy)
def _handle_remove_service_for_activity(self, service, buddy):
(uid, ignore) = service.get_activity_uid()
uid = service.get_activity_uid()
if not uid:
uid = "*"
if self._activity_services.has_key(uid):
@ -231,28 +227,34 @@ class PresenceService(gobject.GObject):
except:
pass
def _resolve_service_reply_cb(self, interface, protocol, name, stype, domain, host, aprotocol, address, port, txt, flags):
def _resolve_service_reply_cb(self, interface, protocol, name, full_stype, domain, host, aprotocol, address, port, txt, flags):
"""When the service discovery finally gets here, we've got enough information about the
service to assign it to a buddy."""
self._log("resolved service '%s' type '%s' domain '%s' to %s:%s" % (name, stype, domain, address, port))
self._log("resolved service '%s' type '%s' domain '%s' to %s:%s" % (name, full_stype, domain, address, port))
name = name.encode()
full_stype = full_stype.encode()
domain = domain.encode()
host = host.encode()
address = address.encode()
# If this service was previously unresolved, remove it from the
# unresolved list
adv_list = self._find_service_adv(interface=interface, protocol=protocol, name=name,
stype=stype, domain=domain)
adv_list = self._find_service_adv(interface=interface, protocol=protocol,
name=name, stype=full_stype, domain=domain)
if not adv_list:
return False
adv = adv_list[0]
adv.set_resolved(True)
# Update the service now that it's been resolved
service = Service.Service(name=name, stype=stype, domain=domain,
service = Service.Service(name=name, stype=full_stype, domain=domain,
address=address, port=port, properties=txt)
adv.set_service(service)
# Merge the service into our buddy and group lists, if needed
buddy = self._handle_new_service_for_buddy(service)
(uid, ignore) = service.get_activity_uid()
uid = service.get_activity_uid()
if buddy and uid:
self._handle_new_service_for_activity(service, buddy)
@ -272,15 +274,15 @@ class PresenceService(gobject.GObject):
error_handler=self._resolve_service_error_handler)
return False
def _service_appeared_cb(self, interface, protocol, name, stype, domain, flags):
self._log("found service '%s' (%d) of type '%s' in domain '%s' on %i.%i." % (name, flags, stype, domain, interface, protocol))
def _service_appeared_cb(self, interface, protocol, name, full_stype, domain, flags):
self._log("found service '%s' (%d) of type '%s' in domain '%s' on %i.%i." % (name, flags, full_stype, domain, interface, protocol))
# Add the service to our unresolved services list
adv_list = self._find_service_adv(interface=interface, protocol=protocol,
name=name, stype=stype, domain=domain)
name=name.encode(), stype=full_stype.encode(), domain=domain.encode())
if not adv_list:
adv = ServiceAdv(interface=interface, protocol=protocol, name=name,
stype=stype, domain=domain)
adv = ServiceAdv(interface=interface, protocol=protocol, name=name.encode(),
stype=full_stype.encode(), domain=domain.encode())
self._service_advs.append(adv)
# Find out the IP address of this interface, if we haven't already
@ -292,30 +294,31 @@ class PresenceService(gobject.GObject):
self._local_addrs[interface] = addr
# Decompose service type if we can
(uid, stype) = Service._decompose_service_type(stype)
# FIXME: find a better way of letting StartPage get all activity advertisements
self.emit('new-service-adv', uid, stype)
(uid, short_stype) = Service._decompose_service_type(full_stype.encode())
# If we care about the service right now, resolve it
resolve = False
if uid in self._activity_uids:
if stype in self._allowed_service_types:
if uid in self._allowed_activities:
if short_stype in self._allowed_service_types:
resolve = True
if self._is_special_service_type(stype):
if self._is_special_service_type(short_stype):
resolve = True
if resolve:
gobject.idle_add(self._resolve_service, interface, protocol, name, stype, domain, flags)
gobject.idle_add(self._resolve_service, interface, protocol, name, full_stype, domain, flags)
return False
def _service_appeared_cb_glue(self, interface, protocol, name, stype, domain, flags):
gobject.idle_add(self._service_appeared_cb, interface, protocol, name, stype, domain, flags)
def _service_disappeared_cb(self, interface, protocol, name, stype, domain, flags):
self._log("service '%s' of type '%s' in domain '%s' on %i.%i disappeared." % (name, stype, domain, interface, protocol))
def _service_disappeared_cb(self, interface, protocol, name, full_stype, domain, flags):
self._log("service '%s' of type '%s' in domain '%s' on %i.%i disappeared." % (name, full_stype, domain, interface, protocol))
name = name.encode()
full_stype = full_stype.encode()
domain = domain.encode()
# If it's an unresolved service, remove it from our unresolved list
adv_list = self._find_service_adv(interface=interface, protocol=protocol,
name=name, stype=stype, domain=domain)
name=name, stype=full_stype, domain=domain)
if not adv_list:
return False
@ -390,48 +393,58 @@ class PresenceService(gobject.GObject):
def _new_domain_cb_glue(self, interface, protocol, domain, flags=0):
gobject.idle_add(self._new_domain_cb, interface, protocol, domain, flags)
def track_service_type(self, stype):
def track_service_type(self, short_stype):
"""Requests that the Presence service look for and recognize
a certain mDNS service types."""
if not self._started:
raise RuntimeError("presence service must be started first.")
if type(stype) != type("") and type(stype) != type(u""):
if type(short_stype) == type(u""):
raise ValueError("service type should not be unicode.")
if type(short_stype) != type(""):
raise ValueError("service type must be a string.")
if type(stype) == type(u""):
stype = stype.encode()
if self._is_special_service_type(stype):
if self._is_special_service_type(short_stype):
return
if stype in self._allowed_service_types:
if short_stype in self._allowed_service_types:
return
# Decompose service type if we can
(uid, dec_stype) = Service._decompose_service_type(stype)
if uid and util.validate_activity_uid(uid):
if uid not in self._activity_uids:
self._activity_uids.append(uid)
(uid, dec_stype) = Service._decompose_service_type(short_stype)
if uid:
raise RuntimeError("Can only track plain service types!")
self._allowed_service_types.append(dec_stype)
# Find unresolved services that match the service type
# we're now interested in, and resolve them
adv_list = self._find_service_adv(stype=stype)
for adv in adv_list:
resolv_list = []
# Find all services first by their activity
for uid in self._allowed_activities:
full_stype = Service.compose_service_type(dec_stype, uid)
adv_list = self._find_service_adv(stype=full_stype)
resolv_list = resolv_list + adv_list
# Then, find services by just the plain service type
resolv_list = resolv_list + self._find_service_adv(stype=dec_stype)
# Request resolution for them
for adv in resolv_list:
gobject.idle_add(self._resolve_service, adv.interface(),
adv.protocol(), adv.name(), adv.stype(), adv.domain(), 0)
def untrack_service_type(self, stype):
def untrack_service_type(self, short_stype):
"""Stop tracking a certain mDNS service."""
if not self._started:
raise RuntimeError("presence service must be started first.")
if not type(stype) == type(""):
if type(short_stype) == type(u""):
raise ValueError("service type should not be unicode.")
if not type(short_stype) == type(""):
raise ValueError("service type must be a string.")
# Decompose service type if we can
(uid, stype) = Service._decompose_service_type(stype)
if uid and util.validate_activity_uid(uid):
if uid in self._activity_uids:
self._activity_uids.remove(uid)
if stype in self._allowed_service_types:
self._allowed_service_types.remove(stype)
(uid, dec_stype) = Service._decompose_service_type(short_stype)
if uid:
raise RuntimeError("Can only untrack plain service types!")
if dec_stype in self._allowed_service_types:
self._allowed_service_types.remove(dec_stype)
def join_shared_activity(self, service):
"""Convenience function to join a group and notify other buddies
@ -442,9 +455,13 @@ class PresenceService(gobject.GObject):
def share_activity(self, activity, stype, properties={}, address=None, port=None):
"""Convenience function to share an activity with other buddies."""
print "type stype == %s" % type(stype)
print "Type of a string is %s" % type("")
uid = activity.get_id()
owner_nick = self._owner.get_nick_name()
real_stype = Service.compose_service_type(stype, uid)
print "PS: type of real_stype is %s" % type(real_stype)
if address and type(address) != type(""):
raise ValueError("address must be a valid string.")
if not address:
@ -470,7 +487,7 @@ class PresenceService(gobject.GObject):
raise RuntimeError("presence service must be started first.")
rs_name = service.get_name()
rs_stype = service.get_type()
rs_stype = service.get_full_type()
rs_port = service.get_port()
if type(rs_port) != type(1) and (rs_port <= 1024 or rs_port > 65536):
raise ValueError("invalid service port.")
@ -493,7 +510,8 @@ class PresenceService(gobject.GObject):
# should un-register it an re-register with the correct info
if str(exc) == "Local name collision":
pass
(uid, activity_stype) = service.get_activity_uid()
uid = service.get_activity_uid()
activity_stype = service.get_type()
self.track_service_type(activity_stype)
return group

View File

@ -22,9 +22,12 @@ def _txt_to_dict(txt):
def compose_service_type(stype, activity_uid):
if not activity_uid:
return stype
if not stype or (type(stype) != type("") and type(stype) != type(u"")):
if type(stype) == type(u""):
raise ValueError("stype must not be in unicode.")
if not stype or type(stype) != type(""):
raise ValueError("stype must be a valid string.")
return "_%s_%s" % (activity_uid, stype)
composed = "_%s_%s" % (activity_uid, stype)
return composed.encode()
def _decompose_service_type(stype):
"""Break a service type into the UID and real service type, if we can."""
@ -60,33 +63,35 @@ class Service(object):
"""Encapsulates information about a specific ZeroConf/mDNS
service as advertised on the network."""
def __init__(self, name, stype, domain, address=None, port=-1, properties=None):
full_stype = stype
# Validate immutable options
if not name or (type(name) != type("") and type(name) != type(u"")) or not len(name):
if name and type(name) == type(u""):
raise ValueError("name must not be in unicode.")
if not name or type(name) != type("") or not len(name):
raise ValueError("must specify a valid service name.")
if not stype or (type(stype) != type("") and type(stype) != type(u"")) or not len(stype):
print "Service:: type of full_stype is %s" % type(full_stype)
if full_stype and type(full_stype) == type(u""):
raise ValueError("service type must not be in unicode.")
if not full_stype or type(full_stype) != type("") or not len(full_stype):
raise ValueError("must specify a service type.")
if not stype.endswith("._tcp") and not stype.endswith("._udp"):
raise ValueError("must specify a TCP or UDP service type.")
if type(domain) != type("") and type(domain) != type(u""):
if domain and type(domain) == type(u""):
raise ValueError("domain must not be in unicode.")
if type(domain) != type(""):
raise ValueError("must specify a domain.")
if len(domain) and domain != "local" and domain != u"local":
if len(domain) and domain != "local":
raise ValueError("must use the 'local' domain (for now).")
if type(stype) == type(u""):
stype = stype.encode()
(uid, real_stype) = _decompose_service_type(stype)
(uid, short_stype) = _decompose_service_type(full_stype)
if uid and not util.validate_activity_uid(uid):
raise ValueError("service type activity uid not a valid activity UID.")
if type(name) == type(u""):
name = name.encode()
self._name = name
self._stype = stype
self._activity_stype = real_stype
if type(domain) == type(u""):
domain = domain.encode()
self._full_stype = full_stype
self._activity_stype = short_stype
self._domain = domain
self._address = None
self.set_address(address)
@ -143,8 +148,16 @@ class Service(object):
self._properties[key] = self._properties[key].encode()
def get_type(self):
"""Return the service's service type."""
return self._stype
"""Return the service's service type without any activity identifiers."""
return self._activity_stype
def get_full_type(self):
"""Return the service's full service type as seen over the network."""
return self._full_stype
def get_activity_uid(self):
"""Return the activity UID this service is associated with, if any."""
return self._activity_uid
def get_port(self):
return self._port
@ -171,10 +184,6 @@ class Service(object):
"""Return the ZeroConf/mDNS domain the service was found in."""
return self._domain
def get_activity_uid(self):
"""Return the activity UID this service is associated with, if any."""
return (self._activity_uid, self._activity_stype)
#################################################################
# Tests

View File

@ -20,7 +20,7 @@ class ActivitiesModel(gtk.ListStore):
self.append([ title, address, None, None ])
def add_activity(self, buddy, service):
(uid, stype) = service.get_activity_uid()
uid = service.get_activity_uid()
title = urllib.unquote(service.get_one_property('Title'))
address = urllib.unquote(service.get_one_property('URI'))
subtitle = 'Shared by %s' % buddy.get_nick_name()
@ -75,7 +75,6 @@ class StartPage(gtk.HBox):
self._pservice = PresenceService.get_instance()
self._pservice.connect("activity-announced", self._on_activity_announced_cb)
self._pservice.connect("new-service-adv", self._on_new_service_adv_cb)
self._pservice.start()
self._pservice.track_service_type(BrowserActivity._BROWSER_ACTIVITY_TYPE)
@ -115,12 +114,6 @@ class StartPage(gtk.HBox):
self.pack_start(sw)
sw.show()
def _on_new_service_adv_cb(self, pservice, uid, stype):
if uid is not None:
real_stype = Service.compose_service_type(stype, uid)
print "Will ask to track uid=%s, stype=%s" % (uid, stype)
self._pservice.track_service_type(real_stype)
def _on_activity_announced_cb(self, pservice, service, buddy):
self._activities.get_model().add_activity(buddy, service)