diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | ce4a32fe52ef09d8f5ff1dd22c001110902b60a2 (patch) | |
tree | 5ac38a06f3dde268dc7927dc155896926aaf7012 /dnssd | |
download | tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.tar.gz tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'dnssd')
-rw-r--r-- | dnssd/INSTALL | 21 | ||||
-rw-r--r-- | dnssd/Mainpage.dox | 40 | ||||
-rw-r--r-- | dnssd/Makefile.am | 22 | ||||
-rw-r--r-- | dnssd/README | 15 | ||||
-rw-r--r-- | dnssd/configure.in.bot | 9 | ||||
-rw-r--r-- | dnssd/configure.in.in | 38 | ||||
-rw-r--r-- | dnssd/domainbrowser.cpp | 168 | ||||
-rw-r--r-- | dnssd/domainbrowser.h | 105 | ||||
-rw-r--r-- | dnssd/kcm_kdnssd.kcfg | 39 | ||||
-rw-r--r-- | dnssd/publicservice.cpp | 230 | ||||
-rw-r--r-- | dnssd/publicservice.h | 149 | ||||
-rw-r--r-- | dnssd/query.cpp | 140 | ||||
-rw-r--r-- | dnssd/query.h | 103 | ||||
-rw-r--r-- | dnssd/remoteservice.cpp | 197 | ||||
-rw-r--r-- | dnssd/remoteservice.h | 111 | ||||
-rw-r--r-- | dnssd/responder.cpp | 108 | ||||
-rw-r--r-- | dnssd/responder.h | 76 | ||||
-rw-r--r-- | dnssd/sdevent.h | 81 | ||||
-rw-r--r-- | dnssd/servicebase.cpp | 115 | ||||
-rw-r--r-- | dnssd/servicebase.h | 121 | ||||
-rw-r--r-- | dnssd/servicebrowser.cpp | 232 | ||||
-rw-r--r-- | dnssd/servicebrowser.h | 227 | ||||
-rw-r--r-- | dnssd/settings.kcfgc | 11 |
23 files changed, 2358 insertions, 0 deletions
diff --git a/dnssd/INSTALL b/dnssd/INSTALL new file mode 100644 index 000000000..9722f975d --- /dev/null +++ b/dnssd/INSTALL @@ -0,0 +1,21 @@ +Installing Apple mDNSResponder: + +WARNING: this is NOT Howl's mDNSResponder and it does not come in Debian package called mdnsresponder. +You can see the difference by checking daemon's name: Apple's one (the correct one) is named 'mdnsd' +Howl's one is named 'mDNSResponder'. + +1) download mDNSResponder + - from Apple site (http://www.opensource.apple.com/darwinsource/tarballs/apsl/mDNSResponder-107.tar.gz) + - or you can get tarball from: http://helios.et.put.poznan.pl/~jstachow/pub/mDNSResponder-107.tar.gz + (for those who don't like registration) + +2) compile and install + Build system for mDNSResponder is quite weird so here are instructions: + + cd mDNSPosix + make os=linux (make without parameters gives list of supported systems) + now as root: + make os=linux install + + make sure that mdnsd.sh init script is properly installed and will be executed at boot time + diff --git a/dnssd/Mainpage.dox b/dnssd/Mainpage.dox new file mode 100644 index 000000000..76b0c1f9b --- /dev/null +++ b/dnssd/Mainpage.dox @@ -0,0 +1,40 @@ +/** @mainpage DNSSD + +<p>DNSSD is a library for handling the DNS-based Service Discovery Protocol (DNS-SD), +the layer of <a href="http://www.zeroconf.org">Zeroconf</a> that allows network +services, such as printers, to be discovered without any user intervention or +centralized infrastructure.</p> + +Apple's implementation of Zeroconf is +<a href="http://www.apple.com/macosx/technology/bonjour.html">Bonjour</a>. +Apple's developer documentation provides lots of information about Bonjour +in its <a href="http://developer.apple.com/documentation/Cocoa/Conceptual/NetServices/Articles/about.html#//apple_ref/doc/uid/TP40002458-SW1">Bonjour overview</a>. + +If you are writing an application that wants to discover services on the network, +use DNSSD::ServiceBrowser. You can also find available service types using ServiceTypeBrowser. + +If you want to announce the availability of a service provided by your application, +use DNSSD::PublicService. + +DNSSD::DomainBrowser allows you to find domains (other than the local one) recommended +for browsing or publishing to. + +Note that DNSSD::ServiceBrowser::isAvailable() provides information about the availability +of the services provided by this library generally, not just for browsing services. + +@authors +Jakub Stachowski + +@maintainers +Jakub Stachowski + +@licenses +@lgpl + +*/ + +// KDE5: get rid of kdeui reference when settings.kcfgc is changed +// DOXYGEN_REFERENCES = kdecore kdeui +// DOXYGEN_SET_PROJECT_NAME = DNSSD +// DOXYGEN_SET_EXCLUDE_PATTERNS += */dnssd/avahi* */dnssd/mdnsd* +// vim:ts=4:sw=4:expandtab:filetype=doxygen diff --git a/dnssd/Makefile.am b/dnssd/Makefile.am new file mode 100644 index 000000000..5c9edb9c4 --- /dev/null +++ b/dnssd/Makefile.am @@ -0,0 +1,22 @@ +# set the include path for X, qt and KDE +INCLUDES = -I$(top_srcdir) $(all_includes) + +# these are the headers for your project +noinst_HEADERS = sdevent.h + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +lib_LTLIBRARIES = libkdnssd.la + +libkdnssd_la_SOURCES = remoteservice.cpp responder.cpp servicebase.cpp \ + settings.kcfgc publicservice.cpp query.cpp domainbrowser.cpp servicebrowser.cpp +dnssdincludedir = $(includedir)/dnssd +dnssdinclude_HEADERS = domainbrowser.h query.h remoteservice.h \ + publicservice.h servicebase.h servicebrowser.h settings.h +libkdnssd_la_LIBADD = ../kdecore/libkdecore.la $(LIB_DNSSD) +libkdnssd_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) -version-info 1:0 + +#kde_kcfg_DATA = kcm_kdnssd.kcfg + +include ../admin/Doxyfile.am diff --git a/dnssd/README b/dnssd/README new file mode 100644 index 000000000..fd085df95 --- /dev/null +++ b/dnssd/README @@ -0,0 +1,15 @@ +Checklist to ensure that zeroconf will work: + +1) Install Apple's mdnsd, at least version 85 +2) kdelibs (and kdebase for ksysguard) should be configured and compiled with dns_sd sdk (part +of mdnsd installation) present - config.h should contain '#define HAVE_DNSSD 1' +3) check /etc/nsswitch.conf and ensure that there is 'mdns' before 'dns' in +line starting with 'host:'. It should be something like: +host: files mdns dns +4) ensure that mdnsd is being started in initscripts +5) for testing: use kpf kicker applet to publish a directory, then open 'zeroconf:/' +URL in konqueror. You should be able to see a local webserver with that published dir. + +For more information go here + +http://wiki.kde.org/tiki-index.php?page=Zeroconf%20in%20KDE diff --git a/dnssd/configure.in.bot b/dnssd/configure.in.bot new file mode 100644 index 000000000..1e2971f8d --- /dev/null +++ b/dnssd/configure.in.bot @@ -0,0 +1,9 @@ +if test "$have_libdns_sd" = "no"; then + echo "" + echo "You're missing Apple mDNSResponder 85 or later, therefore" + echo "dnssd will be compiled as stub, without any real functionality." + echo "If you want zeroconf support (www.zeroconf.org), you should install mDNSResponder first." + echo "See dnssd/INSTALL for details." + echo "" + all_tests=bad +fi diff --git a/dnssd/configure.in.in b/dnssd/configure.in.in new file mode 100644 index 000000000..022ffe871 --- /dev/null +++ b/dnssd/configure.in.in @@ -0,0 +1,38 @@ +#MIN_CONFIG + +AC_ARG_ENABLE(dnssd, [ --disable-dnssd don't require libdns_sd (browsing and publishing DNS-SD services will not be possible) ], with_dnssd=$enableval, with_dnssd=yes) +if test "$with_dnssd" = "yes"; then +AC_MSG_CHECKING(for DNS-SD support) +save_dnssdtest_LIBS="$LIBS" +save_dnssdtest_LDFLAGS="$LDFLAGS" +save_dnssdtest_CPPFLAGS="$CPPFLAGS" +LDFLAGS="$all_libraries $LDFLAGS" +CPPFLAGS="$CPPFLAGS $all_includes" +case $host_os in + darwin*) LIBS="" ;; + *) LIBS="-ldns_sd" ;; +esac +have_libdns_sd="no" +AC_TRY_LINK( [ + #include <dns_sd.h> + ],[ + DNSServiceRefDeallocate( (DNSServiceRef) 0); + TXTRecordDeallocate( (TXTRecordRef*) 0); + ],[ + AC_DEFINE(HAVE_DNSSD,1,[Define if dns-sd is available]) + case $host_os in + darwin*) LIB_DNSSD="" ;; + *) LIB_DNSSD="-ldns_sd" ;; + esac + have_libdns_sd="yes" + AC_MSG_RESULT(yes) + ],[ + AC_MSG_RESULT(no) + LIB_DNSSD="" +]) +CPPFLAGS=$save_dnssdtest_CPPFLAGS +LDFLAGS=$save_dnssdtest_LDFLAGS +LIBS=$save_dnssdtest_LIBS +fi +AC_SUBST(LIB_DNSSD) +AM_CONDITIONAL(HAVE_DNSSD, test "$have_libdns_sd" = "yes") diff --git a/dnssd/domainbrowser.cpp b/dnssd/domainbrowser.cpp new file mode 100644 index 000000000..cad3307dd --- /dev/null +++ b/dnssd/domainbrowser.cpp @@ -0,0 +1,168 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2004 Jakub Stachowski <qbast@go2.pl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <qstringlist.h> +#include "domainbrowser.h" +#include "settings.h" +#include "sdevent.h" +#include "responder.h" +#include "remoteservice.h" +#include "query.h" +#include "servicebrowser.h" +#include <kapplication.h> + +namespace DNSSD +{ + +#ifdef HAVE_DNSSD +void domain_callback(DNSServiceRef, DNSServiceFlags flags, uint32_t, DNSServiceErrorType errorCode, + const char *replyDomain, void *context); +#endif + +class DomainBrowserPrivate : public Responder +{ +public: + DomainBrowserPrivate(DomainBrowser* owner) : Responder(), m_browseLAN(false), m_started(false), m_owner(owner) {} + QStringList m_domains; + virtual void customEvent(QCustomEvent* event); + bool m_browseLAN; + bool m_started; + DomainBrowser* m_owner; +}; + +void DomainBrowserPrivate::customEvent(QCustomEvent* event) +{ + if (event->type()==QEvent::User+SD_ERROR) stop(); + if (event->type()==QEvent::User+SD_ADDREMOVE) { + AddRemoveEvent *aev = static_cast<AddRemoveEvent*>(event); + if (aev->m_op==AddRemoveEvent::Add) m_owner->gotNewDomain(aev->m_domain); + else m_owner->gotRemoveDomain(aev->m_domain); + } +} + +DomainBrowser::DomainBrowser(QObject *parent) : QObject(parent) +{ + d = new DomainBrowserPrivate(this); + d->m_domains = Configuration::domainList(); + if (Configuration::browseLocal()) { + d->m_domains+="local."; + d->m_browseLAN=true; + } + connect(KApplication::kApplication(),SIGNAL(kipcMessage(int,int)),this, + SLOT(domainListChanged(int,int))); +} + +DomainBrowser::DomainBrowser(const QStringList& domains, bool recursive, QObject *parent) : QObject(parent) +{ + d = new DomainBrowserPrivate(this); + d->m_browseLAN = recursive; + d->m_domains=domains; +} + + +DomainBrowser::~DomainBrowser() +{ + delete d; +} + + +void DomainBrowser::startBrowse() +{ + if (d->m_started) return; + d->m_started=true; + if (ServiceBrowser::isAvailable()!=ServiceBrowser::Working) return; + QStringList::const_iterator itEnd = d->m_domains.end(); + for (QStringList::const_iterator it=d->m_domains.begin(); it!=itEnd; ++it ) emit domainAdded(*it); +#ifdef HAVE_DNSSD + if (d->m_browseLAN) { + DNSServiceRef ref; + if (DNSServiceEnumerateDomains(&ref,kDNSServiceFlagsBrowseDomains,0,domain_callback, + reinterpret_cast<void*>(d))==kDNSServiceErr_NoError) d->setRef(ref); + } +#endif +} + +void DomainBrowser::gotNewDomain(const QString& domain) +{ + if (d->m_domains.contains(domain)) return; + d->m_domains.append(domain); + emit domainAdded(domain); +} + +void DomainBrowser::gotRemoveDomain(const QString& domain) +{ + d->m_domains.remove(domain); + emit domainRemoved(domain); +} + +void DomainBrowser::domainListChanged(int message,int) +{ + if (message!=KIPCDomainsChanged) return; + bool was_started = d->m_started; + if (d->isRunning()) d->stop(); // LAN query + d->m_started = false; + // remove all domains and resolvers + if (was_started) { + QStringList::const_iterator itEnd = d->m_domains.end(); + for (QStringList::const_iterator it=d->m_domains.begin(); it!=itEnd; ++it ) + emit domainRemoved(*it); + } + d->m_domains.clear(); + // now reread configuration and add domains + Configuration::self()->readConfig(); + d->m_browseLAN = Configuration::browseLocal(); + d->m_domains = Configuration::domainList(); + if (Configuration::browseLocal()) d->m_domains+="local."; + // this will emit domainAdded() for every domain if necessary + if (was_started) startBrowse(); +} + +const QStringList& DomainBrowser::domains() const +{ + return d->m_domains; +} + +bool DomainBrowser::isRunning() const +{ + return d->m_started; +} + +void DomainBrowser::virtual_hook(int, void*) +{} + +#ifdef HAVE_DNSSD +void domain_callback(DNSServiceRef, DNSServiceFlags flags, uint32_t, DNSServiceErrorType errorCode, + const char *replyDomain, void *context) +{ + QObject *obj = reinterpret_cast<QObject*>(context); + if (errorCode != kDNSServiceErr_NoError) { + ErrorEvent err; + QApplication::sendEvent(obj, &err); + } else { + AddRemoveEvent arev((flags & kDNSServiceFlagsAdd) ? AddRemoveEvent::Add : + AddRemoveEvent::Remove, QString::null, QString::null, + DNSToDomain(replyDomain), !(flags & kDNSServiceFlagsMoreComing)); + QApplication::sendEvent(obj, &arev); + } +} +#endif + +} +#include "domainbrowser.moc" diff --git a/dnssd/domainbrowser.h b/dnssd/domainbrowser.h new file mode 100644 index 000000000..825422d8e --- /dev/null +++ b/dnssd/domainbrowser.h @@ -0,0 +1,105 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2004 Jakub Stachowski <qbast@go2.pl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef DNSSDDOMAINBROWSER_H +#define DNSSDDOMAINBROWSER_H + +#include <qobject.h> +#include <qdict.h> +#include <dnssd/remoteservice.h> + +// KIPC message ID used by kcm module to signal change in browsing domains list + +#define KIPCDomainsChanged 2014 + +class QStringList; +namespace DNSSD +{ +class DomainBrowserPrivate; + +/** +@short Class used to provide current list of domains for browsing. +@author Jakub Stachowski +*/ +class KDNSSD_EXPORT DomainBrowser : public QObject +{ + Q_OBJECT +public: + /** + Standard constructor. It takes all parameters from global configuration. + All changes in configuration are applied immediately. + @param parent Parent object. + */ + DomainBrowser(QObject *parent=0); + + /** + Constructor that creates browser for domain list. This does not use global + configuration at all. + @param domains List of domains + @param recursive TRUE - additionally local network will be browsed for more domains + @param parent Parent object. + This process is recursive. + */ + DomainBrowser(const QStringList& domains, bool recursive=false, QObject *parent=0); + + ~DomainBrowser(); + + /** + Current list of domains to browse. + */ + const QStringList& domains() const; + + /** + Starts browsing. To stop destroy this object. + */ + void startBrowse() ; + + /** + Returns true when browse has already started + */ + bool isRunning() const; + +signals: + /** + Emitted when domain has been removed from browsing list + */ + void domainRemoved(const QString&); + /** + New domain has been discovered. Also emitted for domain specified in constructor + and in global configuration + */ + void domainAdded(const QString&); + +protected: + virtual void virtual_hook(int,void*); +private: + friend class DomainBrowserPrivate; + DomainBrowserPrivate *d; + + void gotNewDomain(const QString&); + void gotRemoveDomain(const QString&); + +private slots: + void domainListChanged(int,int); +}; + +} + +#endif diff --git a/dnssd/kcm_kdnssd.kcfg b/dnssd/kcm_kdnssd.kcfg new file mode 100644 index 000000000..207ab608c --- /dev/null +++ b/dnssd/kcm_kdnssd.kcfg @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile name="kdnssdrc" /> + <group name="browsing" > + <entry key="DomainList" type="StringList" > + <label>Additional domains for browsing</label> + <whatsthis>List of 'wide-area' (non link-local) domains that should be browsed.</whatsthis> + </entry> + <entry key="BrowseLocal" type="Bool" > + <label>Browse local network</label> + <whatsthis>If true .local domain will be browsed. It is always link-local, using multicast DNS.</whatsthis> + <default>true</default> + </entry> + <entry key="Recursive" type="Bool" > + <label>Recursive search for domains</label> + <whatsthis>Removed in KDE 3.5.0</whatsthis> + <default>false</default> + </entry> + </group> + <group name="publishing" > + <entry key="PublishType" type="Enum" > + <label>Select publishing in LAN (multicast) or WAN (unicast, needs configured DNS server)</label> + <whatsthis>Specifies if publishing should be by default link-local using multicast DNS (LAN) or 'wide-area' using normal DNS server (WAN).</whatsthis> + <default>LAN</default> + <choices> + <choice name="LAN" /> + <choice name="WAN" /> + </choices> + </entry> + <entry key="PublishDomain" type="String" > + <label>Name of default publishing domain for WAN</label> + <whatsthis>Domain name for publishing using 'wide-area' (normal DNS) ZeroConf. This must match domain specified in /etc/mdnsd.conf. This value is used only if PublishType is set to WAN. +</whatsthis> + </entry> + </group> +</kcfg> diff --git a/dnssd/publicservice.cpp b/dnssd/publicservice.cpp new file mode 100644 index 000000000..7ba749c53 --- /dev/null +++ b/dnssd/publicservice.cpp @@ -0,0 +1,230 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2004, 2005 Jakub Stachowski <qbast@go2.pl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "publicservice.h" +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#include <netinet/in.h> +#include <sys/socket.h> +#include <qapplication.h> +#include <network/ksocketaddress.h> +#include <kurl.h> +#include <unistd.h> +#include "sdevent.h" +#include "responder.h" +#include "servicebrowser.h" +#include "settings.h" + +namespace DNSSD +{ +static unsigned long publicIP(); +#ifdef HAVE_DNSSD +void publish_callback (DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, + const char*, const char*, void *context); +#endif +class PublicServicePrivate : public Responder +{ +public: + PublicServicePrivate() : m_published(false) + {} + bool m_published; +}; + +PublicService::PublicService(const QString& name, const QString& type, unsigned int port, + const QString& domain) + : QObject(), ServiceBase(name, type, QString::null, domain, port) +{ + d = new PublicServicePrivate; + if (domain.isNull()) + if (Configuration::publishType()==Configuration::EnumPublishType::LAN) m_domain="local."; + else m_domain=Configuration::publishDomain(); +} + + +PublicService::~PublicService() +{ + stop(); + delete d; +} + +void PublicService::setServiceName(const QString& serviceName) +{ + m_serviceName = serviceName; + if (d->isRunning()) { + stop(); + publishAsync(); + } +} + +void PublicService::setDomain(const QString& domain) +{ + m_domain = domain; + if (d->isRunning()) { + stop(); + publishAsync(); + } +} + + +void PublicService::setType(const QString& type) +{ + m_type = type; + if (d->isRunning()) { + stop(); + publishAsync(); + } +} + +void PublicService::setPort(unsigned short port) +{ + m_port = port; + if (d->isRunning()) { + stop(); + publishAsync(); + } +} + +bool PublicService::isPublished() const +{ + return d->m_published; +} + +void PublicService::setTextData(const QMap<QString,QString>& textData) +{ + m_textData = textData; + if (d->isRunning()) { + stop(); + publishAsync(); + } +} + +bool PublicService::publish() +{ + publishAsync(); + while (d->isRunning() && !d->m_published) d->process(); + return d->m_published; +} + +void PublicService::stop() +{ + d->stop(); + d->m_published = false; +} + +void PublicService::publishAsync() +{ + if (d->isRunning()) stop(); +#ifdef HAVE_DNSSD + if (ServiceBrowser::isAvailable()==ServiceBrowser::Working) { + TXTRecordRef txt; + TXTRecordCreate(&txt,0,0); + QMap<QString,QString>::ConstIterator itEnd = m_textData.end(); + for (QMap<QString,QString>::ConstIterator it = m_textData.begin(); it!=itEnd ; ++it) { + QCString value = it.data().utf8(); + if (TXTRecordSetValue(&txt,it.key().utf8(),value.length(),value)!=kDNSServiceErr_NoError) { + TXTRecordDeallocate(&txt); + emit published(false); + return; + } + } + DNSServiceRef ref; + if (DNSServiceRegister(&ref,0,0,m_serviceName.utf8(),m_type.ascii(),domainToDNS(m_domain),NULL, + htons(m_port),TXTRecordGetLength(&txt),TXTRecordGetBytesPtr(&txt),publish_callback, + reinterpret_cast<void*>(this)) == kDNSServiceErr_NoError) d->setRef(ref); + TXTRecordDeallocate(&txt); + } +#endif + if (!d->isRunning()) emit published(false); +} + +#ifdef HAVE_DNSSD +void publish_callback (DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, + const char*, const char*, void *context) +{ + QObject *obj = reinterpret_cast<QObject*>(context); + if (errorCode != kDNSServiceErr_NoError) { + ErrorEvent err; + QApplication::sendEvent(obj, &err); + } else { + PublishEvent pev(QString::fromUtf8(name)); + QApplication::sendEvent(obj, &pev); + } +} +#endif + +const KURL PublicService::toInvitation(const QString& host) +{ + KURL url; + url.setProtocol("invitation"); + if (host.isEmpty()) { // select best address + unsigned long s_address = publicIP(); + if (!s_address) return KURL(); + KNetwork::KIpAddress addr(s_address); + url.setHost(addr.toString()); + } else url.setHost(host); + //FIXME: if there is no public interface, select any non-loopback + url.setPort(m_port); + url.setPath("/"+m_type+"/"+KURL::encode_string(m_serviceName)); + QString query; + QMap<QString,QString>::ConstIterator itEnd = m_textData.end(); + for (QMap<QString,QString>::ConstIterator it = m_textData.begin(); it!=itEnd ; ++it) + url.addQueryItem(it.key(),it.data());; + return url; +} + +void PublicService::customEvent(QCustomEvent* event) +{ + if (event->type()==QEvent::User+SD_ERROR) { + stop(); + emit published(false); + } + if (event->type()==QEvent::User+SD_PUBLISH) { + d->m_published=true; + emit published(true); + m_serviceName = static_cast<PublishEvent*>(event)->m_name; + } +} + +void PublicService::virtual_hook(int, void*) +{ +} + +static unsigned long publicIP() +{ + struct sockaddr_in addr; + socklen_t len = sizeof(addr); + int sock = socket(AF_INET,SOCK_DGRAM,0); + if (sock == -1) return 0; + addr.sin_family = AF_INET; + addr.sin_port = 1; // Not important, any port and public address will do + addr.sin_addr.s_addr = 0x11111111; + if ((connect(sock,(const struct sockaddr*)&addr,sizeof(addr))) == -1) { close(sock); return 0; } + if ((getsockname(sock,(struct sockaddr*)&addr, &len)) == -1) { close(sock); return 0; } + ::close(sock); + return addr.sin_addr.s_addr; +} + + +} + +#include "publicservice.moc" diff --git a/dnssd/publicservice.h b/dnssd/publicservice.h new file mode 100644 index 000000000..480f071ee --- /dev/null +++ b/dnssd/publicservice.h @@ -0,0 +1,149 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2004, 2005 Jakub Stachowski <qbast@go2.pl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef DNSSDPUBLICSERVICE_H +#define DNSSDPUBLICSERVICE_H + +#include <qobject.h> +#include <dnssd/servicebase.h> + +class KURL; +namespace DNSSD +{ +class PublicServicePrivate; + +/** +This class is most important for application that wants to announce its service on network. +Suppose that you want to make your web server public - this is simplest way: + +\code +DNSSD::PublicService *service = new DNSSD::PublicService("My files","_http._tcp",80); +bool isOK = service->publish(); +\endcode + +In this example publish() is synchronous - it will not return until publishing is complete. +This is usually not too long but it can freeze application's GUI for a moment. +Asynchronous publishing is better for responsiveness. Example: + +\code +DNSSD::PublicService *service = new DNSSD::PublicService("My files","_http._tcp",80); +connect(service,SIGNAL(published(bool)),this,SLOT(wasPublished(bool))); +service->publishAsync(); +\endcode + + +@short This class represents local service being published +@author Jakub Stachowski + */ + +class KDNSSD_EXPORT PublicService : public QObject, public ServiceBase +{ + Q_OBJECT +public: + /** + @param name Service name. If set to QString::null, computer name will be used and will be + available via serviceName() after successful registration + @param type Service type. Has to be in form _sometype._udp or _sometype._tcp + @param port Port number. Set to 0 to "reserve" service name. + @param domain Domain name. If left as QString:null, user configuration will be used. "local." + means local LAN + */ + PublicService(const QString& name=QString::null,const QString& type=QString::null, + unsigned int port=0,const QString& domain=QString::null); + + ~PublicService(); + + /** + Stops publishing or abort incomplete publish request. Useful when you want to disable service + for some time. + */ + void stop(); + + /** + Synchrounous publish. Application will be freezed until publishing is complete. + @return true if successfull. + */ + bool publish(); + + /** + Returns true is currently published + */ + bool isPublished() const; + + /** + Asynchronous version of publish(). It return immediately and emits signal published(bool) + when completed. Note that in case of early detected error (like bad service type) signal may be + emitted before return of this function. + */ + void publishAsync(); + + /** + Sets new text properties. If services is already published, it will be re-announced with new data. + */ + void setTextData(const QMap<QString,QString>& textData); + + /** + Sets name of the service. If service is currently published, it will be re-announced with new data. + */ + void setServiceName(const QString& serviceName); + + /** + Sets type of service. It has to in form of _type._udp or _type._tcp. If service is + currently published, it will be re-announced with new data. + */ + void setType(const QString& type); + + /** + Sets port. If service is currently published, it will be re-announced with new data. + */ + void setPort(unsigned short port); + + /** + Sets domain where service is published. "local." means local LAN. If service is currently + published, it will be re-announced with new data. + */ + void setDomain(const QString& domain); + + /** + Translates service into URL that can be sent to another user. + @param host Use specified hostname. If left empty, public IP address (the one used for + default route) will be used. + @since 3.5 + */ + const KURL toInvitation(const QString& host=QString::null); + +signals: + /** + Emitted when publishing is complete - parameter is set to true if it was successfull. It will also + emitted when name, port or type of already published service is changed. + */ + void published(bool); +private: + PublicServicePrivate *d; + +protected: + virtual void customEvent(QCustomEvent* event); + virtual void virtual_hook(int, void*); +}; + + +} + +#endif diff --git a/dnssd/query.cpp b/dnssd/query.cpp new file mode 100644 index 000000000..bcb9c69f5 --- /dev/null +++ b/dnssd/query.cpp @@ -0,0 +1,140 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2004 Jakub Stachowski <qbast@go2.pl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "query.h" +#include "responder.h" +#include "remoteservice.h" +#include "sdevent.h" +#include <kdebug.h> +#include <qapplication.h> +#include <qtimer.h> + +#define TIMEOUT_WAN 2000 +#define TIMEOUT_LAN 200 + +namespace DNSSD +{ +#ifdef HAVE_DNSSD +void query_callback (DNSServiceRef, DNSServiceFlags flags, uint32_t, DNSServiceErrorType errorCode, + const char *serviceName, const char *regtype, const char *replyDomain, void *context); +#endif +class QueryPrivate : public Responder +{ +public: + QueryPrivate(const QString& type, const QString& domain) : Responder(), m_finished(false), + m_domain(domain), m_type(type) + {}; + bool m_finished; + QString m_domain; + QTimer timeout; + QString m_type; +}; + +Query::Query(const QString& type, const QString& domain) +{ + d = new QueryPrivate(type,domain); + connect(&d->timeout,SIGNAL(timeout()),this,SLOT(timeout())); +} + + +Query::~Query() +{ + delete d; +} + +bool Query::isRunning() const +{ + return d->isRunning(); +} + +bool Query::isFinished() const +{ + return d->m_finished; +} + +const QString& Query::domain() const +{ + return d->m_domain; +} + +void Query::startQuery() +{ + if (d->isRunning()) return; + d->m_finished = false; +#ifdef HAVE_DNSSD + DNSServiceRef ref; + if (DNSServiceBrowse(&ref,0,0, d->m_type.ascii(), + domainToDNS(d->m_domain),query_callback,reinterpret_cast<void*>(this)) + == kDNSServiceErr_NoError) d->setRef(ref); +#endif + if (!d->isRunning()) emit finished(); + else d->timeout.start(domainIsLocal(d->m_domain) ? TIMEOUT_LAN : TIMEOUT_WAN,true); +} +void Query::virtual_hook(int, void*) +{ +} + +void Query::customEvent(QCustomEvent* event) +{ + if (event->type()==QEvent::User+SD_ERROR) { + d->stop(); + d->m_finished=false; + emit finished(); + } + if (event->type()==QEvent::User+SD_ADDREMOVE) { + RemoteService* svr; + AddRemoveEvent *aev = static_cast<AddRemoveEvent*>(event); + // m_type has useless trailing dot + QString type=aev->m_type.left(aev->m_type.length()-1); + // label is badly splitted here - _http _tcp.local. . - rely on decode() + if (d->m_type=="_services._dns-sd._udp") svr = new RemoteService(aev->m_name+"."+ + type+"."+aev->m_domain); + else svr = new RemoteService(aev->m_name, type, aev->m_domain); + if (aev->m_op==AddRemoveEvent::Add) emit serviceAdded(svr); + else emit serviceRemoved(svr); + d->m_finished = aev->m_last; + if (d->m_finished) emit finished(); + } +} + +void Query::timeout() +{ + d->m_finished=true; + emit finished(); +} +#ifdef HAVE_DNSSD +void query_callback (DNSServiceRef, DNSServiceFlags flags, uint32_t, DNSServiceErrorType errorCode, + const char *serviceName, const char *regtype, const char *replyDomain, + void *context) +{ + QObject *obj = reinterpret_cast<QObject*>(context); + if (errorCode != kDNSServiceErr_NoError) { + ErrorEvent err; + QApplication::sendEvent(obj, &err); + } else { + AddRemoveEvent arev((flags & kDNSServiceFlagsAdd) ? AddRemoveEvent::Add : + AddRemoveEvent::Remove, QString::fromUtf8(serviceName), regtype, + DNSToDomain(replyDomain), !(flags & kDNSServiceFlagsMoreComing)); + QApplication::sendEvent(obj, &arev); + } +} +#endif +} +#include "query.moc" diff --git a/dnssd/query.h b/dnssd/query.h new file mode 100644 index 000000000..793b18731 --- /dev/null +++ b/dnssd/query.h @@ -0,0 +1,103 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2004 Jakub Stachowski <qbast@go2.pl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef DNSSDQUERY_H +#define DNSSDQUERY_H + +#include <qobject.h> +#include <dnssd/remoteservice.h> + + +namespace DNSSD +{ +class QueryPrivate; + +/** +This class provides way to search for specified service type in one domain. Depending on domain +name, either multicast or unicast DNS will be used. + +@short Class that represents service query in one domain. +@author Jakub Stachowski + */ +class KDNSSD_EXPORT Query : public QObject +{ + Q_OBJECT +public: + /** + Creates new query. + + @param type Type of services to browse for + @param domain Domain name - if set to "local." multicast query will be performed, + otherwise unicast + */ + Query(const QString& type, const QString& domain); + + virtual ~Query(); + + /** + Starts query. Ignored if query is already running + */ + virtual void startQuery(); + + /** + Returns TRUE if query is already running + */ + bool isRunning() const; + + /** + Returns TRUE if all currently announced services has + been reported. It does not mean that no more services can + be found later and it is not related to isRunning() + */ + bool isFinished() const; + + /** + Returns queried domain + */ + const QString& domain() const; + +signals: + /** + Emitted when new service has been discovered + */ + void serviceAdded(DNSSD::RemoteService::Ptr); + + /** + Emitted when previously discovered service is not longer published + */ + void serviceRemoved(DNSSD::RemoteService::Ptr); + + /** + Emitted when all announced services has been reported. + */ + void finished(); + +protected: + virtual void virtual_hook(int, void*); + virtual void customEvent(QCustomEvent* event); +private: + QueryPrivate *d; +private slots: + void timeout(); +}; + +} + +#endif diff --git a/dnssd/remoteservice.cpp b/dnssd/remoteservice.cpp new file mode 100644 index 000000000..9d3e4abb6 --- /dev/null +++ b/dnssd/remoteservice.cpp @@ -0,0 +1,197 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2004, 2005 Jakub Stachowski <qbast@go2.pl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <config.h> + +#include <qeventloop.h> +#include <qapplication.h> +#include <kurl.h> +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#include <netinet/in.h> +#include "remoteservice.h" +#include "responder.h" +#include "sdevent.h" +#include <kdebug.h> + +namespace DNSSD +{ +#ifdef HAVE_DNSSD +void resolve_callback ( DNSServiceRef, + DNSServiceFlags, + uint32_t, + DNSServiceErrorType errorCode, + const char*, + const char *hosttarget, + uint16_t port, + uint16_t txtLen, + const unsigned char *txtRecord, + void *context + ); + +#endif +class RemoteServicePrivate : public Responder +{ +public: + RemoteServicePrivate() : Responder(), m_resolved(false) + {}; + bool m_resolved; +}; + +RemoteService::RemoteService(const QString& label) +{ + decode(label); + d = new RemoteServicePrivate(); +} +RemoteService::RemoteService(const QString& name,const QString& type,const QString& domain) + : ServiceBase(name, type, domain) +{ + d = new RemoteServicePrivate(); +} + +RemoteService::RemoteService(const KURL& url) +{ + d = new RemoteServicePrivate(); + if (!url.isValid()) return; + if (url.protocol()!="invitation") return; + if (!url.hasPath()) return; + m_hostName = url.host(); + m_port = url.port(); + m_type = url.path().section('/',1,1); + m_serviceName = url.path().section('/',2); + m_textData = url.queryItems(); + d->m_resolved=true; +} + +RemoteService::~RemoteService() +{ + delete d; +} + +bool RemoteService::resolve() +{ + resolveAsync(); + while (d->isRunning() && !d->m_resolved) d->process(); + d->stop(); + return d->m_resolved; +} + +void RemoteService::resolveAsync() +{ + if (d->isRunning()) return; + d->m_resolved = false; + kdDebug() << this << ":Starting resolve of : " << m_serviceName << " " << m_type << " " << m_domain << "\n"; +#ifdef HAVE_DNSSD + DNSServiceRef ref; + if (DNSServiceResolve(&ref,0,0,m_serviceName.utf8(), m_type.ascii(), + domainToDNS(m_domain),(DNSServiceResolveReply)resolve_callback,reinterpret_cast<void*>(this)) + == kDNSServiceErr_NoError) d->setRef(ref); +#endif + if (!d->isRunning()) emit resolved(false); +} + +bool RemoteService::isResolved() const +{ + return d->m_resolved; +} + +void RemoteService::customEvent(QCustomEvent* event) +{ + if (event->type() == QEvent::User+SD_ERROR) { + d->stop(); + d->m_resolved=false; + emit resolved(false); + } + if (event->type() == QEvent::User+SD_RESOLVE) { + ResolveEvent* rev = static_cast<ResolveEvent*>(event); + m_hostName = rev->m_hostname; + m_port = rev->m_port; + m_textData = rev->m_txtdata; + d->m_resolved = true; + emit resolved(true); + } +} + +void RemoteService::virtual_hook(int, void*) +{ + // BASE::virtual_hook(int, void*); +} + +QDataStream & operator<< (QDataStream & s, const RemoteService & a) +{ + s << (static_cast<ServiceBase>(a)); + Q_INT8 resolved = a.d->m_resolved ? 1:0; + s << resolved; + return s; +} + +QDataStream & operator>> (QDataStream & s, RemoteService & a) +{ + // stop any possible resolve going on + a.d->stop(); + Q_INT8 resolved; + operator>>(s,(static_cast<ServiceBase&>(a))); + s >> resolved; + a.d->m_resolved = (resolved == 1); + return s; +} + + +#ifdef HAVE_DNSSD +void resolve_callback ( DNSServiceRef, + DNSServiceFlags, + uint32_t, + DNSServiceErrorType errorCode, + const char*, + const char *hosttarget, + uint16_t port, + uint16_t txtLen, + const unsigned char *txtRecord, + void *context + ) +{ + QObject *obj = reinterpret_cast<QObject*>(context); + if (errorCode != kDNSServiceErr_NoError) { + ErrorEvent err; + QApplication::sendEvent(obj, &err); + return; + } + char key[256]; + int index=0; + unsigned char valueLen; + kdDebug() << "Resolve callback\n"; + QMap<QString,QString> map; + const void *voidValue = 0; + while (TXTRecordGetItemAtIndex(txtLen,txtRecord,index++,256,key,&valueLen, + &voidValue) == kDNSServiceErr_NoError) + { + if (voidValue) map[QString::fromUtf8(key)]=QString::fromUtf8((const char*)voidValue,valueLen); + else map[QString::fromUtf8(key)]=QString::null; + } + ResolveEvent rev(DNSToDomain(hosttarget),ntohs(port),map); + QApplication::sendEvent(obj, &rev); +} +#endif + + +} + +#include "remoteservice.moc" diff --git a/dnssd/remoteservice.h b/dnssd/remoteservice.h new file mode 100644 index 000000000..220331b5f --- /dev/null +++ b/dnssd/remoteservice.h @@ -0,0 +1,111 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2004, 2005 Jakub Stachowski <qbast@go2.pl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef DNSSDREMOTESERVICE_H +#define DNSSDREMOTESERVICE_H + +#include <qobject.h> +#include <dnssd/servicebase.h> + +class QDataStream; +class KURL; +namespace DNSSD +{ +class RemoteServicePrivate; + +/** +RemoteService class allows to resolve service announced on remote machine. In most cases objects +of this class are created by ServiceBrowser, but it is not required. Only fields valid before +service is resolved are name, type.and domain. + + +@short class representing service announced on remote machine. +@author Jakub Stachowski + */ +class KDNSSD_EXPORT RemoteService : public QObject, public ServiceBase +{ + Q_OBJECT +public: + typedef KSharedPtr<RemoteService> Ptr; + + /** + Creates unresolved service from given DNS label + @param label Data returned by PTR query - it is decoded into name, type and + domain + */ + RemoteService(const QString& label); + + /** + Creates unresolved remote service with given name, type and domain. + */ + RemoteService(const QString& name,const QString& type,const QString& domain); + + /** + Creates resolved remote service from invitation URL constructed by PublicService::toInvitation. + If URL was invalid, service is set to unresolved and other fields should not be used. + */ + RemoteService(const KURL& url); + + virtual ~RemoteService(); + + /** + Resolves host name and port of service. Host name is not resolved into numeric + address - use KResolver for that. Signal resolved(bool) will be emitted + when finished or even before return of this function - in case of immediate failure. + */ + void resolveAsync(); + + /** + Synchronous version of resolveAsync(). Note that resolved(bool) is emitted + before this function returns, + @return TRUE is successful + */ + bool resolve(); + + /** + Returns true if service has been successfully resolved + */ + bool isResolved() const; + +signals: + /** + Emitted when resolving is complete. Parameter is set to TRUE if it was successful. + If operating in asynchronous mode this signal can be emitted several times (when + service change) + */ + void resolved(bool); + +protected: + virtual void virtual_hook(int id, void *data); + virtual void customEvent(QCustomEvent* event); +private: + void resolveError(); + void resolved(const char *host, unsigned short port, unsigned short txtlen, + const char* txtRecord); + RemoteServicePrivate *d; + + friend KDNSSD_EXPORT QDataStream & operator<< (QDataStream & s, const RemoteService & a); + friend KDNSSD_EXPORT QDataStream & operator>> (QDataStream & s, RemoteService & a); + +}; + +} + +#endif diff --git a/dnssd/responder.cpp b/dnssd/responder.cpp new file mode 100644 index 000000000..3721d3aae --- /dev/null +++ b/dnssd/responder.cpp @@ -0,0 +1,108 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2004 Jakub Stachowski <qbast@go2.pl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "responder.h" +#include <qapplication.h> +#include <kidna.h> + +// dns_sd.h API should care about proper encoding of non-latin1 characters +// but for now it does not +#define IDN_BROKEN_IN_MDNSRESPONDER + +namespace DNSSD +{ + +Responder::Responder(DNSServiceRef ref,QObject *parent, const char *name) + : QObject(parent, name), m_ref(0), m_socket(0) +{ + setRef(ref); +} + +void Responder::setRef(DNSServiceRef ref) +{ + if (m_socket || m_ref) stop(); + m_running = false; + m_ref = ref; + if (m_ref == 0 ) return; +#ifdef HAVE_DNSSD + int fd = DNSServiceRefSockFD(ref); + if (fd == -1) return; + m_socket = new QSocketNotifier(fd,QSocketNotifier::Read,this); + connect(m_socket,SIGNAL(activated(int)),this,SLOT(process())); + m_running = true; +#endif +} +Responder::~Responder() +{ + stop(); +} + +void Responder::stop() +{ + if (m_socket) delete m_socket; + m_socket = 0; +#ifdef HAVE_DNSSD + if (m_ref) DNSServiceRefDeallocate(m_ref); +#endif + m_ref = 0; + m_running = false; +} + + +void Responder::process() +{ +#ifdef HAVE_DNSSD + if ( DNSServiceProcessResult(m_ref) != kDNSServiceErr_NoError) stop(); +#endif +} + +bool Responder::isRunning() const +{ + return m_running; +} + +bool domainIsLocal(const QString& domain) +{ + return domain.section('.',-1,-1).lower()=="local"; +} + +QCString domainToDNS(const QString &domain) +{ +#ifdef IDN_BROKEN_IN_MDNSRESPONDER + if (domainIsLocal(domain)) return domain.utf8(); + else return KIDNA::toAsciiCString(domain); +#else + return domain.utf8(); +#endif +} + +QString DNSToDomain(const char* domain) +{ +#ifdef IDN_BROKEN_IN_MDNSRESPONDER + if (domainIsLocal(domain)) return QString::fromUtf8(domain); + else return KIDNA::toUnicode(domain); +#else + return QString::fromUtf8(domain); +#endif +} + + +} +#include "responder.moc" diff --git a/dnssd/responder.h b/dnssd/responder.h new file mode 100644 index 000000000..6e36d3357 --- /dev/null +++ b/dnssd/responder.h @@ -0,0 +1,76 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2004 Jakub Stachowski <qbast@go2.pl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef DNSSDRESPONDER_H +#define DNSSDRESPONDER_H + +#include <qobject.h> +#include <qsocketnotifier.h> +#include <qsignal.h> +#include <config.h> +#ifdef HAVE_DNSSD +#include <dns_sd.h> +#else +#define DNSServiceRef void* +#endif + +namespace DNSSD +{ + +/** +This class should not be used directly. + +@author Jakub Stachowski +@short Internal class wrapping dns_sd.h interface + */ +class Responder : public QObject +{ + Q_OBJECT + +public: + Responder(DNSServiceRef ref=0,QObject *parent = 0, const char *name = 0); + + ~Responder(); + + /** + Returns true if it is possible to use mDNS service publishing and discovery. + It needs mDNSResponder running. + */ + bool isRunning() const; + void setRef(DNSServiceRef ref); + void stop(); +public slots: + void process(); +protected: + DNSServiceRef m_ref; + bool m_running; + QSocketNotifier *m_socket; +}; + +/* Utils functions */ + +bool domainIsLocal(const QString& domain); +// Encodes domain name using utf8() or IDN +QCString domainToDNS(const QString &domain); +QString DNSToDomain(const char* domain); + +} + +#endif diff --git a/dnssd/sdevent.h b/dnssd/sdevent.h new file mode 100644 index 000000000..5104839a7 --- /dev/null +++ b/dnssd/sdevent.h @@ -0,0 +1,81 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2004 Jakub Stachowski <qbast@go2.pl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef DNSSDSDEVENT_H +#define DNSSDSDEVENT_H + +#include <qevent.h> +#include <qstring.h> +#include <qmap.h> + +namespace DNSSD +{ + +enum Operation { SD_ERROR = 101,SD_ADDREMOVE, SD_PUBLISH, SD_RESOLVE}; + +class ErrorEvent : public QCustomEvent +{ +public: + ErrorEvent() : QCustomEvent(QEvent::User+SD_ERROR) + {} +}; +class AddRemoveEvent : public QCustomEvent +{ +public: + enum Operation { Add, Remove }; + AddRemoveEvent(Operation op,const QString& name,const QString& type, + const QString& domain, bool last) : QCustomEvent(QEvent::User+SD_ADDREMOVE), + m_op(op), m_name(name), m_type(type), m_domain(domain), m_last(last) + {} + + const Operation m_op; + const QString m_name; + const QString m_type; + const QString m_domain; + const bool m_last; +}; + +class PublishEvent : public QCustomEvent +{ +public: + PublishEvent(const QString& name) : QCustomEvent(QEvent::User+SD_PUBLISH), m_name(name) + {} + + const QString m_name; +}; + +class ResolveEvent : public QCustomEvent +{ +public: + ResolveEvent(const QString& hostname, unsigned short port, + const QMap<QString,QString>& txtdata) + : QCustomEvent(QEvent::User+SD_RESOLVE), m_hostname(hostname), + m_port(port), m_txtdata(txtdata) + {} + + const QString m_hostname; + const unsigned short m_port; + const QMap<QString,QString> m_txtdata; +}; + + +} + +#endif diff --git a/dnssd/servicebase.cpp b/dnssd/servicebase.cpp new file mode 100644 index 000000000..ef2943b95 --- /dev/null +++ b/dnssd/servicebase.cpp @@ -0,0 +1,115 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2004 Jakub Stachowski <qbast@go2.pl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "servicebase.h" +#include <qregexp.h> + +namespace DNSSD +{ + +ServiceBase::ServiceBase(const QString& name, const QString& type, const QString& domain, + const QString& host, unsigned short port) : + m_serviceName(name), m_type(type), m_domain(domain), m_hostName(host), m_port(port) +{} + +ServiceBase::~ServiceBase() +{} + +QString ServiceBase::encode() +{ + return m_serviceName.replace("\\","\\\\").replace(".","\\.") + QString(".") + m_type + + QString(".") + m_domain; +} + +// example: 3rd\.\032Floor\032Copy\032Room._ipp._tcp.dns-sd.org. - normal service +// 3rd\.\032Floor\032Copy\032Room.dns-sd.org - domain +// _ipp._tcp.dns-sd.org - metaquery + +void ServiceBase::decode(const QString& name) +{ + QString rest; + if (name[0]=='_') { // metaquery + m_serviceName=""; + rest=name; + } else { // normal service or domain + QString decoded_name=name; + decoded_name=decoded_name.replace("\\\\","\\"); + int i = decoded_name.find(QRegExp("[^\\\\]\\.")); + if (i==-1) return; // first find service name + rest = decoded_name.mid(i+2); + m_serviceName=decoded_name.left(i+1).replace("\\.","."); + } + m_type = rest.section('.',0,1); + // does it really have a type? + if (m_type[0]=='_' && m_type[m_type.find('.')+1]=='_') + m_domain = rest.section('.',2,-1,QString::SectionIncludeTrailingSep); + else { + m_type=""; + m_domain=rest; + } +} + +const QString& ServiceBase::serviceName() const +{ + return m_serviceName; +} + +const QString& ServiceBase::type() const +{ + return m_type; +} + +const QString& ServiceBase::domain() const +{ + return m_domain; +} + +const QString& ServiceBase::hostName() const +{ + return m_hostName; +} + +unsigned short ServiceBase::port() const +{ + return m_port; +} +const QMap<QString,QString>& ServiceBase::textData() const +{ + return m_textData; +} + +void ServiceBase::virtual_hook(int, void*) +{} + +QDataStream & operator<< (QDataStream & s, const ServiceBase & a) +{ + s << a.m_serviceName << a.m_type << a.m_domain << a.m_hostName << Q_INT16(a.m_port) << a.m_textData; + return s; +} + +QDataStream & operator>> (QDataStream & s, ServiceBase & a) +{ + Q_INT16 port; + s >> a.m_serviceName >> a.m_type >> a.m_domain >> a.m_hostName >> port >> a.m_textData; + a.m_port = port; + return s; +} + +} diff --git a/dnssd/servicebase.h b/dnssd/servicebase.h new file mode 100644 index 000000000..28ec7f565 --- /dev/null +++ b/dnssd/servicebase.h @@ -0,0 +1,121 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2004 Jakub Stachowski <qbast@go2.pl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef DNSSDSERVICEBASE_H +#define DNSSDSERVICEBASE_H + +#include <qmap.h> +#include <ksharedptr.h> + +class QString; +class QDataStream; +namespace DNSSD +{ +class ServiceBasePrivate; + +/** +This class is used to carry information about service. It can be remote, local, +metaservice or domain. Metaservice has only type and domain - it means that +services of given type are present in given domain. +@short Describes any type of service. +@author Jakub Stachowski + */ +class KDNSSD_EXPORT ServiceBase : public KShared +{ +public: + typedef KSharedPtr<ServiceBase> Ptr; + + /** + @param name Service name - empty for metaservices + @param type Service type - empty for domains + @param domain Domain name + @param host Host name + @param port Port number + */ + ServiceBase(const QString& name=QString::null,const QString& type=QString::null, + const QString& domain=QString::null, const QString& host=QString::null, + unsigned short port=0); + + virtual ~ServiceBase(); + + /** + Returns name of service. This is empty for metaservices + */ + const QString& serviceName() const; + + /** + Returns type of service. It always in format _sometype._udp or _sometype._tcp and + it is empty for domains. + */ + const QString& type() const; + + /** + Returns domain that given service belongs to. It is "local." for link-local services. + */ + const QString& domain() const; + + /** + Returns hostname. It is only valid for local and resolved remote services. + */ + const QString& hostName() const; + + /** + Returns port number. It is only valid for local and resolved remote services. + */ + unsigned short port() const; + + /** + Returns read only map of text properties. It is only valid for local and resolved remote services. + */ + const QMap<QString,QString>& textData() const; + +protected: + QString m_serviceName; + QString m_type; + QString m_domain; + QString m_hostName; + unsigned short m_port; + + /** + Map of TXT properties + */ + QMap<QString,QString> m_textData; + /** + Encode service name, type and domain into string that can be used as DNS-SD PTR label + */ + QString encode(); + /** + Decode PTR label returned by DNS resolver into service name, type and domain. It also + handles special cases - metaservices and domains. + */ + void decode(const QString& name); + + friend KDNSSD_EXPORT QDataStream & operator<< (QDataStream & s, const ServiceBase & a); + friend KDNSSD_EXPORT QDataStream & operator>> (QDataStream & s, ServiceBase & a); + + virtual void virtual_hook(int, void*); +private: + ServiceBasePrivate* d; + +}; + +} + +#endif diff --git a/dnssd/servicebrowser.cpp b/dnssd/servicebrowser.cpp new file mode 100644 index 000000000..017dc8102 --- /dev/null +++ b/dnssd/servicebrowser.cpp @@ -0,0 +1,232 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2004 Jakub Stachowski <qbast@go2.pl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <signal.h> +#include <errno.h> +#include <qstringlist.h> +#include <qfile.h> +#include "domainbrowser.h" +#include "query.h" +#include "servicebrowser.h" +#include <config.h> +#ifdef HAVE_DNSSD +#include <dns_sd.h> +#endif + +#define MDNSD_PID "/var/run/mdnsd.pid" + +namespace DNSSD +{ + +const QString ServiceBrowser::AllServices = "_services._dns-sd._udp"; + +class ServiceBrowserPrivate +{ +public: + ServiceBrowserPrivate() : m_running(false) + {} + QValueList<RemoteService::Ptr> m_services; + QValueList<RemoteService::Ptr> m_duringResolve; + QStringList m_types; + DomainBrowser* m_domains; + int m_flags; + bool m_running; + bool m_finished; + QDict<Query> resolvers; +}; + +ServiceBrowser::ServiceBrowser(const QString& type,DomainBrowser* domains,bool autoResolve) +{ + if (domains) init(type,domains,autoResolve ? AutoResolve : 0); + else init(type,new DomainBrowser(this),autoResolve ? AutoResolve|AutoDelete : AutoDelete); +} +ServiceBrowser::ServiceBrowser(const QStringList& types,DomainBrowser* domains,int flags) +{ + if (domains) init(types,domains,flags); + else init(types,new DomainBrowser(this),flags|AutoDelete); +} + +void ServiceBrowser::init(const QStringList& type,DomainBrowser* domains,int flags) +{ + d = new ServiceBrowserPrivate(); + d->resolvers.setAutoDelete(true); + d->m_types=type; + d->m_flags=flags; + d->m_domains = domains; + connect(d->m_domains,SIGNAL(domainAdded(const QString& )),this,SLOT(addDomain(const QString& ))); + connect(d->m_domains,SIGNAL(domainRemoved(const QString& )),this, + SLOT(removeDomain(const QString& ))); +} +ServiceBrowser::ServiceBrowser(const QString& type,const QString& domain,bool autoResolve) +{ + init(type,new DomainBrowser(domain,false,this),autoResolve ? AutoResolve|AutoDelete : AutoDelete); +} +ServiceBrowser::ServiceBrowser(const QString& type,const QString& domain,int flags) +{ + init(type,new DomainBrowser(domain,false,this),flags | AutoDelete); +} + +const ServiceBrowser::State ServiceBrowser::isAvailable() +{ +#ifdef HAVE_DNSSD + QFile f(MDNSD_PID); + if (!f.open(IO_ReadOnly)) return Stopped; // no pidfile + QString line; + if (f.readLine(line,16)<1) return Stopped; + unsigned int pid = line.toUInt(); + if (pid==0) return Stopped; // not a pid + return (kill(pid,0)==0 || errno==EPERM) ? Working : Stopped; + // signal 0 only checks if process is running, mdnsd is probably owned by 'nobody' so we will + // get EPERM, if mdnsd is not running error will be ESRCH + +#else + return Unsupported; +#endif +} +ServiceBrowser::~ ServiceBrowser() +{ + if (d->m_flags & AutoDelete) delete d->m_domains; + delete d; +} + +const DomainBrowser* ServiceBrowser::browsedDomains() const +{ + return d->m_domains; +} + +void ServiceBrowser::serviceResolved(bool success) +{ + QObject* sender_obj = const_cast<QObject*>(sender()); + RemoteService* svr = static_cast<RemoteService*>(sender_obj); + disconnect(svr,SIGNAL(resolved(bool)),this,SLOT(serviceResolved(bool))); + QValueList<RemoteService::Ptr>::Iterator it = d->m_duringResolve.begin(); + QValueList<RemoteService::Ptr>::Iterator itEnd = d->m_duringResolve.end(); + while ( it!= itEnd && svr!= (*it)) ++it; + if (it != itEnd) { + if (success) { + d->m_services+=(*it); + emit serviceAdded(svr); + } + d->m_duringResolve.remove(it); + queryFinished(); + } +} + +void ServiceBrowser::startBrowse() +{ + if (d->m_running) return; + d->m_running=true; + if (isAvailable()!=Working) return; + if (d->m_domains->isRunning()) { + QStringList::const_iterator itEnd = d->m_domains->domains().end(); + for ( QStringList::const_iterator it = d->m_domains->domains().begin(); it != itEnd; ++it ) + addDomain(*it); + } else d->m_domains->startBrowse(); +} + +void ServiceBrowser::gotNewService(RemoteService::Ptr svr) +{ + if (findDuplicate(svr)==(d->m_services.end())) { + if (d->m_flags & AutoResolve) { + connect(svr,SIGNAL(resolved(bool )),this,SLOT(serviceResolved(bool ))); + d->m_duringResolve+=svr; + svr->resolveAsync(); + } else { + d->m_services+=svr; + emit serviceAdded(svr); + } + } +} + +void ServiceBrowser::gotRemoveService(RemoteService::Ptr svr) +{ + QValueList<RemoteService::Ptr>::Iterator it = findDuplicate(svr); + if (it!=(d->m_services.end())) { + emit serviceRemoved(*it); + d->m_services.remove(it); + } +} + + +void ServiceBrowser::removeDomain(const QString& domain) +{ + while (d->resolvers[domain]) d->resolvers.remove(domain); + QValueList<RemoteService::Ptr>::Iterator it = d->m_services.begin(); + while (it!=d->m_services.end()) + // use section to skip possible trailing dot + if ((*it)->domain().section('.',0) == domain.section('.',0)) { + emit serviceRemoved(*it); + it = d->m_services.remove(it); + } else ++it; +} + +void ServiceBrowser::addDomain(const QString& domain) +{ + if (!d->m_running) return; + if (!(d->resolvers[domain])) { + QStringList::ConstIterator itEnd = d->m_types.end(); + for (QStringList::ConstIterator it=d->m_types.begin(); it!=itEnd; ++it) { + Query* b = new Query((*it),domain); + connect(b,SIGNAL(serviceAdded(DNSSD::RemoteService::Ptr)),this, + SLOT(gotNewService(DNSSD::RemoteService::Ptr))); + connect(b,SIGNAL(serviceRemoved(DNSSD::RemoteService::Ptr )),this, + SLOT(gotRemoveService(DNSSD::RemoteService::Ptr))); + connect(b,SIGNAL(finished()),this,SLOT(queryFinished())); + b->startQuery(); + d->resolvers.insert(domain,b); + } + } +} + +void ServiceBrowser::queryFinished() +{ + if (allFinished()) emit finished(); +} + +bool ServiceBrowser::allFinished() +{ + if (d->m_duringResolve.count()) return false; + bool all = true; + QDictIterator<Query> it(d->resolvers); + for ( ; it.current(); ++it) all&=(*it)->isFinished(); + return all; +} + +const QValueList<RemoteService::Ptr>& ServiceBrowser::services() const +{ + return d->m_services; +} + +void ServiceBrowser::virtual_hook(int, void*) +{} + +QValueList<RemoteService::Ptr>::Iterator ServiceBrowser::findDuplicate(RemoteService::Ptr src) +{ + QValueList<RemoteService::Ptr>::Iterator itEnd = d->m_services.end(); + for (QValueList<RemoteService::Ptr>::Iterator it = d->m_services.begin(); it!=itEnd; ++it) + if ((src->type()==(*it)->type()) && (src->serviceName()==(*it)->serviceName()) && + (src->domain() == (*it)->domain())) return it; + return itEnd; +} + + +} + +#include "servicebrowser.moc" diff --git a/dnssd/servicebrowser.h b/dnssd/servicebrowser.h new file mode 100644 index 000000000..5a12f4e6b --- /dev/null +++ b/dnssd/servicebrowser.h @@ -0,0 +1,227 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2004 Jakub Stachowski <qbast@go2.pl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef DNSSDSERVICEBROWSER_H +#define DNSSDSERVICEBROWSER_H + +#include <qobject.h> +#include <qdict.h> +#include <dnssd/remoteservice.h> + + +class QStringList; +namespace DNSSD +{ +class DomainBrowser; +class ServiceBrowserPrivate; + +/** +Most important class for applications that want to discover specific services on network. +Suppose that you need list of web servers running. Example: + +\code +DNSSD::ServiceBrowser* browser = new DNSSD::ServiceBrowser("_http._tcp"); +connect(browser,SIGNAL(serviceAdded(RemoteService::Ptr)),this,SLOT(addService(RemoteService::Ptr))); +connect(browser,SIGNAL(serviceRemoved(RemoteService::Ptr)),this,SLOT(delService(RemoteService::Ptr))); +browser->startBrowse(); +\endcode + +In above example addService will be called for every web server already running or just appearing +on network and delService will be called when server is stopped. Because no DomainBrowser was passed +to constructor, domains configured by user will be searched. + + +@author Jakub Stachowski +@short Browsing for specific type of services or all available service types + */ +class KDNSSD_EXPORT ServiceBrowser : public QObject +{ + Q_OBJECT +public: + /** + @li AutoDelete - DomainBrowser object passes in constructor should be deleted when ServiceBrowser is deleted + @li AutoResolve - after disovering new service it will be resolved and then + reported with serviceAdded() signal. It raises network usage by resolving all services, + so use it only when necessary. + */ + enum Flags { + AutoDelete =1, + AutoResolve = 2 + }; + + /** + Availability of DNS-SD services. + @li Working - available + @li Stopped - not available because mdnsd daemon is not running. + @li Unsupported - not available because KDE was compiled without DNS-SD support + */ + enum State { Working, Stopped, Unsupported }; + + /** + ServiceBrowser constructor. + + @param types List of service types to browse for (example: "_http._tcp"). + Can also be DNSSD::ServicesBrowser::AllServices to specify "metaquery" for all service types + present on network + @param domains DomainBrowser object used to specify domains to browse. You do not have to connect + its domainAdded() signal - it will be done automatically. You can left this parameter as NULL + for default domains. + @param flags One or more values from #Flags + + @since 3.5 + @todo KDE4: set default values for domains and flags + */ + ServiceBrowser(const QStringList& types,DomainBrowser* domains,int flags); + + /** + The same as above, but allows only one service type + @param type Type of services to browse for + @param domains DomainBrowser object used to specify domains to browse. You do not have to connect its domainAdded() signal - it will be done automatically. You can left this parameter as NULL for default domains. + @param autoResolve auto resolve, if set + @deprecated use previous constructor instead + */ + ServiceBrowser(const QString& type,DomainBrowser* domains=0,bool autoResolve=false); + + /** + Overloaded constructor used to create browser for one domain + + @param type Type of services to browse for + @param domain Domain name. You can add more domains later using addDomain and remove them + with removeDomain + @param flags One or more values from #Flags. AutoDelete flag has no effect + @since 3.5 + */ + ServiceBrowser(const QString& type,const QString& domain, int flags); + + /** + @deprecated user previous constructor instead + */ + ServiceBrowser(const QString& type,const QString& domain, bool autoResolve=false); + + ~ServiceBrowser(); + + /** + Returns list of services + */ + const QValueList<RemoteService::Ptr>& services() const; + + /** + Starts browsing for services. + To stop it just destroy the object. + */ + virtual void startBrowse(); + + /** + Return DomainBrowser containing domains being browsed. Valid object will returned + even if 'domains' parameters in constructor was set to NULL or single domain + constructor was used. + */ + const DomainBrowser* browsedDomains() const; + + /** + Special service type to search for all available service types. Pass it as "type" + parameter to ServiceBrowser constructor. + */ + static const QString AllServices; + + /** + Checks availability of DNS-SD services (this also covers publishing). + + If you use this function to report an error to the user, below is a suggestion + on how to word the errors: + + \code + switch(DNSSD::ServiceBrowser::isAvailable()) { + case DNSSD::ServiceBrowser::Working: + return ""; + case DNSSD::ServiceBrowser::Stopped: + return i18n("<p>The Zeroconf daemon is not running. See the Service Discovery Handbook" + " for more information.<br/>" + "Other users will not see this system when browsing" + " the network via zeroconf, but normal access will still work.</p>"); + case DNSSD::ServiceBrowser::Unsupported: + return i18n("<p>Zeroconf support is not available in this version of KDE." + " See the Service Discovery Handbook for more information.<br/>" + "Other users will not see this system when browsing" + " the network via zeroconf, but normal access will still work.</p>"); + default: + return i18n("<p>Unknown error with Zeroconf.<br/>" + "Other users will not see this system when browsing" + " the network via zeroconf, but normal access will still work.</p>"); + } + \endcode + + */ + static const State isAvailable(); + +signals: + /** + Emitted when new service is discovered (or resolved + if autoresolve is set + */ + void serviceAdded(DNSSD::RemoteService::Ptr); + /** + Emitted when service is no longer announced. RemoteService object + is deleted from services list and destroyed immediately after this + signal returns. + */ + void serviceRemoved(DNSSD::RemoteService::Ptr); + + /** + Emitted when all services has been reported. This signal can be used + by application that just want to get list of currently available services + (similar to directory listing) and do not care about dynamic adding/removing + services later. This signal can be emitted many time: for example if new host + has been connected to network and is announcing some services interesting to + this ServiceBrowser, they will be reported by several serviceAdded() signals and + whole batch will be concluded by finished(). + */ + void finished(); + +public slots: + /** + Remove one domain from list of domains to browse + */ + void removeDomain(const QString& domain); + + /** + Add new domain to browse + */ + void addDomain(const QString& domain); + +protected: + virtual void virtual_hook(int, void*); +private: + ServiceBrowserPrivate *d; + + bool allFinished(); + void init(const QStringList&, DomainBrowser*, int); + QValueList<RemoteService::Ptr>::Iterator findDuplicate(RemoteService::Ptr src); +private slots: + void serviceResolved(bool success); + void gotNewService(DNSSD::RemoteService::Ptr); + void gotRemoveService(DNSSD::RemoteService::Ptr); + void queryFinished(); + +}; + +} + +#endif diff --git a/dnssd/settings.kcfgc b/dnssd/settings.kcfgc new file mode 100644 index 000000000..df47ad3a5 --- /dev/null +++ b/dnssd/settings.kcfgc @@ -0,0 +1,11 @@ +ClassName=Configuration +File=kcm_kdnssd.kcfg +GlobalEnums=false +Inherits=KConfigSkeleton +ItemAccessors=false +MemberVariables=private +Mutators=true +NameSpace=DNSSD +SetUserTexts=false +Singleton=true +Visibility=KDNSSD_EXPORT |