## common/zeroconf/zeroconf_bonjour.py ## ## Copyright (C) 2006 Stefan Bethge ## ## This file is part of Gajim. ## ## Gajim 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; version 3 only. ## ## Gajim 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 Gajim. If not, see . ## from gajim.common import app import select import re from gajim.common.zeroconf.zeroconf import Constant try: import pybonjour except ImportError: pass resolve_timeout = 1 class Zeroconf: def __init__(self, new_serviceCB, remove_serviceCB, name_conflictCB, disconnected_CB, error_CB, name, host, port): self.domain = None # specific domain to browse self.stype = '_presence._tcp' self.port = port # listening port that gets announced self.username = name self.host = host self.txt = pybonjour.TXTRecord() # service data # XXX these CBs should be set to None when we destroy the object # (go offline), because they create a circular reference self.new_serviceCB = new_serviceCB self.remove_serviceCB = remove_serviceCB self.name_conflictCB = name_conflictCB self.disconnected_CB = disconnected_CB self.error_CB = error_CB self.contacts = {} # all current local contacts with data self.connected = False self.announced = False self.invalid_self_contact = {} self.resolved = [] def browse_callback(self, sdRef, flags, interfaceIndex, errorCode, serviceName, regtype, replyDomain): app.log.debug('Found service %s in domain %s on %i(type: %s).' % (serviceName, replyDomain, interfaceIndex, regtype)) if not self.connected: return if errorCode != pybonjour.kDNSServiceErr_NoError: return if not (flags & pybonjour.kDNSServiceFlagsAdd): self.remove_service_callback(serviceName) return # asynchronous resolving resolve_sdRef = pybonjour.DNSServiceResolve(0, interfaceIndex, serviceName, regtype, replyDomain, self.service_resolved_callback) try: while not self.resolved: ready = select.select([resolve_sdRef], [], [], resolve_timeout) if resolve_sdRef not in ready[0]: app.log.debug('Resolve timed out') break pybonjour.DNSServiceProcessResult(resolve_sdRef) else: self.resolved.pop() finally: resolve_sdRef.close() def remove_service_callback(self, name): app.log.debug('Service %s disappeared.' % name) if not self.connected: return if name != self.name: for key in self.contacts.keys(): if self.contacts[key][Constant.BARE_NAME] == name: del self.contacts[key] self.remove_serviceCB(key) return def new_domain_callback(self, interface, protocol, domain, flags): if domain != "local": self.browse_domain(interface, protocol, domain) # takes a TXTRecord instance def txt_array_to_dict(self, txt): items = pybonjour.TXTRecord.parse(txt)._items return dict((v[0], v[1]) for v in items.values()) def service_resolved_callback(self, sdRef, flags, interfaceIndex, errorCode, fullname, hosttarget, port, txtRecord): # TODO: do proper decoding... escaping= { r'\.': '.', r'\032': ' ', r'\064': '@', } # Split on '.' but do not split on '\.' result = re.split('(?