/* * Copyright (c) 2010-2014 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <boost/bind.hpp> #include <iostream> #include <Swiften/LinkLocal/LinkLocalServiceBrowser.h> #include <Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h> #include <Swiften/Network/HostAddress.h> namespace Swift { LinkLocalServiceBrowser::LinkLocalServiceBrowser(boost::shared_ptr<DNSSDQuerier> querier) : querier(querier), haveError(false) { } LinkLocalServiceBrowser::~LinkLocalServiceBrowser() { if (isRunning()) { std::cerr << "WARNING: LinkLocalServiceBrowser still running on destruction" << std::endl; } } void LinkLocalServiceBrowser::start() { assert(!isRunning()); haveError = false; browseQuery = querier->createBrowseQuery(); browseQuery->onServiceAdded.connect( boost::bind(&LinkLocalServiceBrowser::handleServiceAdded, this, _1)); browseQuery->onServiceRemoved.connect( boost::bind(&LinkLocalServiceBrowser::handleServiceRemoved, this, _1)); browseQuery->onError.connect( boost::bind(&LinkLocalServiceBrowser::handleBrowseError, this)); browseQuery->startBrowsing(); } void LinkLocalServiceBrowser::stop() { assert(isRunning()); if (isRegistered()) { unregisterService(); } for (ResolveQueryMap::const_iterator i = resolveQueries.begin(); i != resolveQueries.end(); ++i) { i->second->stop(); } resolveQueries.clear(); services.clear(); browseQuery->stopBrowsing(); browseQuery.reset(); onStopped(haveError); } bool LinkLocalServiceBrowser::isRunning() const { return !!browseQuery; } bool LinkLocalServiceBrowser::hasError() const { return haveError; } bool LinkLocalServiceBrowser::isRegistered() const { return !!registerQuery; } void LinkLocalServiceBrowser::registerService(const std::string& name, int port, const LinkLocalServiceInfo& info) { assert(!registerQuery); registerQuery = querier->createRegisterQuery(name, port, info.toTXTRecord()); registerQuery->onRegisterFinished.connect( boost::bind(&LinkLocalServiceBrowser::handleRegisterFinished, this, _1)); registerQuery->registerService(); } void LinkLocalServiceBrowser::updateService(const LinkLocalServiceInfo& info) { assert(registerQuery); registerQuery->updateServiceInfo(info.toTXTRecord()); } void LinkLocalServiceBrowser::unregisterService() { assert(registerQuery); registerQuery->unregisterService(); registerQuery.reset(); selfService.reset(); } std::vector<LinkLocalService> LinkLocalServiceBrowser::getServices() const { std::vector<LinkLocalService> result; for (ServiceMap::const_iterator i = services.begin(); i != services.end(); ++i) { result.push_back(LinkLocalService(i->first, i->second)); } return result; } void LinkLocalServiceBrowser::handleServiceAdded(const DNSSDServiceID& service) { if (selfService && service == *selfService) { return; } std::pair<ResolveQueryMap::iterator, bool> r = resolveQueries.insert(std::make_pair(service, boost::shared_ptr<DNSSDResolveServiceQuery>())); if (r.second) { // There was no existing query yet. Start a new query. boost::shared_ptr<DNSSDResolveServiceQuery> resolveQuery = querier->createResolveServiceQuery(service); resolveQuery->onServiceResolved.connect( boost::bind(&LinkLocalServiceBrowser::handleServiceResolved, this, service, _1)); r.first->second = resolveQuery; resolveQuery->start(); } } void LinkLocalServiceBrowser::handleServiceRemoved(const DNSSDServiceID& service) { ResolveQueryMap::iterator i = resolveQueries.find(service); if (i == resolveQueries.end()) { // Can happen after an unregister(), when getting the old 'self' // service remove notification. return; } i->second->stop(); resolveQueries.erase(i); ServiceMap::iterator j = services.find(service); assert(j != services.end()); LinkLocalService linkLocalService(j->first, j->second); services.erase(j); onServiceRemoved(linkLocalService); } void LinkLocalServiceBrowser::handleServiceResolved(const DNSSDServiceID& service, const boost::optional<DNSSDResolveServiceQuery::Result>& result) { if (result) { std::pair<ServiceMap::iterator, bool> r = services.insert(std::make_pair(service, *result)); if (r.second) { onServiceAdded(LinkLocalService(r.first->first, r.first->second)); } else { r.first->second = *result; onServiceChanged(LinkLocalService(r.first->first, r.first->second)); } } } void LinkLocalServiceBrowser::handleRegisterFinished(const boost::optional<DNSSDServiceID>& result) { if (result) { selfService = result; onServiceRegistered(*result); } else { haveError = true; stop(); } } void LinkLocalServiceBrowser::handleBrowseError() { haveError = true; stop(); } }