summaryrefslogtreecommitdiffstats
path: root/dnssd
diff options
context:
space:
mode:
Diffstat (limited to 'dnssd')
-rw-r--r--dnssd/INSTALL21
-rw-r--r--dnssd/Mainpage.dox40
-rw-r--r--dnssd/Makefile.am22
-rw-r--r--dnssd/README15
-rw-r--r--dnssd/configure.in.bot9
-rw-r--r--dnssd/configure.in.in38
-rw-r--r--dnssd/domainbrowser.cpp168
-rw-r--r--dnssd/domainbrowser.h105
-rw-r--r--dnssd/kcm_kdnssd.kcfg39
-rw-r--r--dnssd/publicservice.cpp230
-rw-r--r--dnssd/publicservice.h149
-rw-r--r--dnssd/query.cpp140
-rw-r--r--dnssd/query.h103
-rw-r--r--dnssd/remoteservice.cpp197
-rw-r--r--dnssd/remoteservice.h111
-rw-r--r--dnssd/responder.cpp108
-rw-r--r--dnssd/responder.h76
-rw-r--r--dnssd/sdevent.h81
-rw-r--r--dnssd/servicebase.cpp115
-rw-r--r--dnssd/servicebase.h121
-rw-r--r--dnssd/servicebrowser.cpp232
-rw-r--r--dnssd/servicebrowser.h227
-rw-r--r--dnssd/settings.kcfgc11
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