servicebrowser.cpp
00001 /* This file is part of the KDE project 00002 * 00003 * Copyright (C) 2004 Jakub Stachowski <qbast@go2.pl> 00004 * 00005 * This library is free software; you can redistribute it and/or 00006 * modify it under the terms of the GNU Library General Public 00007 * License as published by the Free Software Foundation; either 00008 * version 2 of the License, or (at your option) any later version. 00009 * 00010 * This library is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 * Library General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Library General Public License 00016 * along with this library; see the file COPYING.LIB. If not, write to 00017 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 * Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include <signal.h> 00022 #include <errno.h> 00023 #include <qstringlist.h> 00024 #include <qfile.h> 00025 #include "domainbrowser.h" 00026 #include "query.h" 00027 #include "servicebrowser.h" 00028 #include <config.h> 00029 #ifdef HAVE_DNSSD 00030 #include <dns_sd.h> 00031 #endif 00032 00033 #define MDNSD_PID "/var/run/mdnsd.pid" 00034 00035 namespace DNSSD 00036 { 00037 00038 const QString ServiceBrowser::AllServices = "_services._dns-sd._udp"; 00039 00040 class ServiceBrowserPrivate 00041 { 00042 public: 00043 ServiceBrowserPrivate() : m_running(false) 00044 {} 00045 QValueList<RemoteService::Ptr> m_services; 00046 QValueList<RemoteService::Ptr> m_duringResolve; 00047 QStringList m_types; 00048 DomainBrowser* m_domains; 00049 int m_flags; 00050 bool m_running; 00051 bool m_finished; 00052 QDict<Query> resolvers; 00053 }; 00054 00055 ServiceBrowser::ServiceBrowser(const QString& type,DomainBrowser* domains,bool autoResolve) 00056 { 00057 if (domains) init(type,domains,autoResolve ? AutoResolve : 0); 00058 else init(type,new DomainBrowser(this),autoResolve ? AutoResolve|AutoDelete : AutoDelete); 00059 } 00060 ServiceBrowser::ServiceBrowser(const QStringList& types,DomainBrowser* domains,int flags) 00061 { 00062 if (domains) init(types,domains,flags); 00063 else init(types,new DomainBrowser(this),flags|AutoDelete); 00064 } 00065 00066 void ServiceBrowser::init(const QStringList& type,DomainBrowser* domains,int flags) 00067 { 00068 d = new ServiceBrowserPrivate(); 00069 d->resolvers.setAutoDelete(true); 00070 d->m_types=type; 00071 d->m_flags=flags; 00072 d->m_domains = domains; 00073 connect(d->m_domains,SIGNAL(domainAdded(const QString& )),this,SLOT(addDomain(const QString& ))); 00074 connect(d->m_domains,SIGNAL(domainRemoved(const QString& )),this, 00075 SLOT(removeDomain(const QString& ))); 00076 } 00077 ServiceBrowser::ServiceBrowser(const QString& type,const QString& domain,bool autoResolve) 00078 { 00079 init(type,new DomainBrowser(domain,false,this),autoResolve ? AutoResolve|AutoDelete : AutoDelete); 00080 } 00081 ServiceBrowser::ServiceBrowser(const QString& type,const QString& domain,int flags) 00082 { 00083 init(type,new DomainBrowser(domain,false,this),flags | AutoDelete); 00084 } 00085 00086 const ServiceBrowser::State ServiceBrowser::isAvailable() 00087 { 00088 #ifdef HAVE_DNSSD 00089 QFile f(MDNSD_PID); 00090 if (!f.open(IO_ReadOnly)) return Stopped; // no pidfile 00091 QString line; 00092 if (f.readLine(line,16)<1) return Stopped; 00093 unsigned int pid = line.toUInt(); 00094 if (pid==0) return Stopped; // not a pid 00095 return (kill(pid,0)==0 || errno==EPERM) ? Working : Stopped; 00096 // signal 0 only checks if process is running, mdnsd is probably owned by 'nobody' so we will 00097 // get EPERM, if mdnsd is not running error will be ESRCH 00098 00099 #else 00100 return Unsupported; 00101 #endif 00102 } 00103 ServiceBrowser::~ ServiceBrowser() 00104 { 00105 if (d->m_flags & AutoDelete) delete d->m_domains; 00106 delete d; 00107 } 00108 00109 const DomainBrowser* ServiceBrowser::browsedDomains() const 00110 { 00111 return d->m_domains; 00112 } 00113 00114 void ServiceBrowser::serviceResolved(bool success) 00115 { 00116 QObject* sender_obj = const_cast<QObject*>(sender()); 00117 RemoteService* svr = static_cast<RemoteService*>(sender_obj); 00118 disconnect(svr,SIGNAL(resolved(bool)),this,SLOT(serviceResolved(bool))); 00119 QValueList<RemoteService::Ptr>::Iterator it = d->m_duringResolve.begin(); 00120 QValueList<RemoteService::Ptr>::Iterator itEnd = d->m_duringResolve.end(); 00121 while ( it!= itEnd && svr!= (*it)) ++it; 00122 if (it != itEnd) { 00123 if (success) { 00124 d->m_services+=(*it); 00125 emit serviceAdded(svr); 00126 } 00127 d->m_duringResolve.remove(it); 00128 queryFinished(); 00129 } 00130 } 00131 00132 void ServiceBrowser::startBrowse() 00133 { 00134 if (d->m_running) return; 00135 d->m_running=true; 00136 if (isAvailable()!=Working) return; 00137 if (d->m_domains->isRunning()) { 00138 QStringList::const_iterator itEnd = d->m_domains->domains().end(); 00139 for ( QStringList::const_iterator it = d->m_domains->domains().begin(); it != itEnd; ++it ) 00140 addDomain(*it); 00141 } else d->m_domains->startBrowse(); 00142 } 00143 00144 void ServiceBrowser::gotNewService(RemoteService::Ptr svr) 00145 { 00146 if (findDuplicate(svr)==(d->m_services.end())) { 00147 if (d->m_flags & AutoResolve) { 00148 connect(svr,SIGNAL(resolved(bool )),this,SLOT(serviceResolved(bool ))); 00149 d->m_duringResolve+=svr; 00150 svr->resolveAsync(); 00151 } else { 00152 d->m_services+=svr; 00153 emit serviceAdded(svr); 00154 } 00155 } 00156 } 00157 00158 void ServiceBrowser::gotRemoveService(RemoteService::Ptr svr) 00159 { 00160 QValueList<RemoteService::Ptr>::Iterator it = findDuplicate(svr); 00161 if (it!=(d->m_services.end())) { 00162 emit serviceRemoved(*it); 00163 d->m_services.remove(it); 00164 } 00165 } 00166 00167 00168 void ServiceBrowser::removeDomain(const QString& domain) 00169 { 00170 while (d->resolvers[domain]) d->resolvers.remove(domain); 00171 QValueList<RemoteService::Ptr>::Iterator it = d->m_services.begin(); 00172 while (it!=d->m_services.end()) 00173 // use section to skip possible trailing dot 00174 if ((*it)->domain().section('.',0) == domain.section('.',0)) { 00175 emit serviceRemoved(*it); 00176 it = d->m_services.remove(it); 00177 } else ++it; 00178 } 00179 00180 void ServiceBrowser::addDomain(const QString& domain) 00181 { 00182 if (!d->m_running) return; 00183 if (!(d->resolvers[domain])) { 00184 QStringList::ConstIterator itEnd = d->m_types.end(); 00185 for (QStringList::ConstIterator it=d->m_types.begin(); it!=itEnd; ++it) { 00186 Query* b = new Query((*it),domain); 00187 connect(b,SIGNAL(serviceAdded(DNSSD::RemoteService::Ptr)),this, 00188 SLOT(gotNewService(DNSSD::RemoteService::Ptr))); 00189 connect(b,SIGNAL(serviceRemoved(DNSSD::RemoteService::Ptr )),this, 00190 SLOT(gotRemoveService(DNSSD::RemoteService::Ptr))); 00191 connect(b,SIGNAL(finished()),this,SLOT(queryFinished())); 00192 b->startQuery(); 00193 d->resolvers.insert(domain,b); 00194 } 00195 } 00196 } 00197 00198 void ServiceBrowser::queryFinished() 00199 { 00200 if (allFinished()) emit finished(); 00201 } 00202 00203 bool ServiceBrowser::allFinished() 00204 { 00205 if (d->m_duringResolve.count()) return false; 00206 bool all = true; 00207 QDictIterator<Query> it(d->resolvers); 00208 for ( ; it.current(); ++it) all&=(*it)->isFinished(); 00209 return all; 00210 } 00211 00212 const QValueList<RemoteService::Ptr>& ServiceBrowser::services() const 00213 { 00214 return d->m_services; 00215 } 00216 00217 void ServiceBrowser::virtual_hook(int, void*) 00218 {} 00219 00220 QValueList<RemoteService::Ptr>::Iterator ServiceBrowser::findDuplicate(RemoteService::Ptr src) 00221 { 00222 QValueList<RemoteService::Ptr>::Iterator itEnd = d->m_services.end(); 00223 for (QValueList<RemoteService::Ptr>::Iterator it = d->m_services.begin(); it!=itEnd; ++it) 00224 if ((src->type()==(*it)->type()) && (src->serviceName()==(*it)->serviceName()) && 00225 (src->domain() == (*it)->domain())) return it; 00226 return itEnd; 00227 } 00228 00229 00230 } 00231 00232 #include "servicebrowser.moc"