# Copyright (C) 2006 Stefan Bethge # Copyright (C) 2006 Philipp Hörist # # 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 . import logging import select import socket import re from gajim.common.zeroconf.zeroconf import Constant log = logging.getLogger('gajim.c.z.zeroconf_bonjour') 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.stype = '_presence._tcp' self.port = port # listening port that gets announced self.username = name self.host = host self.txt = {} # 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_contacts = {} self.resolved = [] self.queried = [] def browse_callback(self, sdRef, flags, interfaceIndex, errorCode, serviceName, regtype, replyDomain): 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: log.debug('Error in browse_callback: %s', str(errorCode)) return if not flags & pybonjour.kDNSServiceFlagsAdd: self.remove_service_callback(serviceName) return try: # asynchronous resolving resolve_sdRef = None resolve_sdRef = pybonjour.DNSServiceResolve( 0, interfaceIndex, serviceName, regtype, replyDomain, self.service_resolved_callback) while not self.resolved: ready = select.select([resolve_sdRef], [], [], resolve_timeout) if resolve_sdRef not in ready[0]: log.info('Resolve timed out') break pybonjour.DNSServiceProcessResult(resolve_sdRef) else: self.resolved.pop() except pybonjour.BonjourError as error: log.info('Error when resolving DNS: %s', error) finally: if resolve_sdRef: resolve_sdRef.close() def remove_service_callback(self, name): log.info('Service %s disappeared.', name) if not self.connected: return if name != self.name: for key in list(self.contacts.keys()): if self.contacts[key][Constant.NAME] == name: del self.contacts[key] self.remove_serviceCB(key) return def txt_array_to_dict(self, txt): if isinstance(txt, pybonjour.TXTRecord): items = txt._items else: items = pybonjour.TXTRecord.parse(txt)._items return dict((v[0], v[1]) for v in items.values()) @staticmethod def _parse_name(fullname): log.debug('Parse name: %s', fullname) # TODO: do proper decoding... escaping = {r'\.': '.', r'\032': ' ', r'\064': '@', } # Split on '.' but do not split on '\.' result = re.split('(?