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 "responder.h" 00027 #include "query.h" 00028 #include "servicebrowser.h" 00029 #include <avahi-client/client.h> 00030 #include <config.h> 00031 00032 namespace DNSSD 00033 { 00034 00035 const QString ServiceBrowser::AllServices = "_services._dns-sd._udp"; 00036 00037 class ServiceBrowserPrivate 00038 { 00039 public: 00040 ServiceBrowserPrivate() : m_running(false) 00041 {} 00042 QValueList<RemoteService::Ptr> m_services; 00043 QValueList<RemoteService::Ptr> m_duringResolve; 00044 QStringList m_types; 00045 DomainBrowser* m_domains; 00046 int m_flags; 00047 bool m_running; 00048 bool m_finished; 00049 QDict<Query> resolvers; 00050 }; 00051 00052 ServiceBrowser::ServiceBrowser(const QString& type,DomainBrowser* domains,bool autoResolve) 00053 { 00054 if (domains) init(type,domains,autoResolve ? AutoResolve : 0); 00055 else init(type,new DomainBrowser(this),autoResolve ? AutoResolve|AutoDelete : AutoDelete); 00056 } 00057 ServiceBrowser::ServiceBrowser(const QStringList& types,DomainBrowser* domains,int flags) 00058 { 00059 if (domains) init(types,domains,flags); 00060 else init(types,new DomainBrowser(this),flags|AutoDelete); 00061 } 00062 00063 void ServiceBrowser::init(const QStringList& type,DomainBrowser* domains,int flags) 00064 { 00065 d = new ServiceBrowserPrivate(); 00066 d->resolvers.setAutoDelete(true); 00067 d->m_types=type; 00068 d->m_flags=flags; 00069 d->m_domains = domains; 00070 connect(d->m_domains,SIGNAL(domainAdded(const QString& )),this,SLOT(addDomain(const QString& ))); 00071 connect(d->m_domains,SIGNAL(domainRemoved(const QString& )),this, 00072 SLOT(removeDomain(const QString& ))); 00073 } 00074 ServiceBrowser::ServiceBrowser(const QString& type,const QString& domain,bool autoResolve) 00075 { 00076 init(type,new DomainBrowser(domain,false,this),autoResolve ? AutoResolve|AutoDelete : AutoDelete); 00077 } 00078 ServiceBrowser::ServiceBrowser(const QString& type,const QString& domain,int flags) 00079 { 00080 init(type,new DomainBrowser(domain,false,this),flags | AutoDelete); 00081 } 00082 00083 const ServiceBrowser::State ServiceBrowser::isAvailable() 00084 { 00085 AvahiClientState s = Responder::self().state(); 00086 #ifdef AVAHI_API_0_6 00087 return (s==AVAHI_CLIENT_FAILURE) ? Stopped : Working; 00088 #else 00089 return (s==AVAHI_CLIENT_S_INVALID || s==AVAHI_CLIENT_DISCONNECTED) ? Stopped : Working; 00090 #endif 00091 } 00092 ServiceBrowser::~ ServiceBrowser() 00093 { 00094 if (d->m_flags & AutoDelete) delete d->m_domains; 00095 delete d; 00096 } 00097 00098 const DomainBrowser* ServiceBrowser::browsedDomains() const 00099 { 00100 return d->m_domains; 00101 } 00102 00103 void ServiceBrowser::serviceResolved(bool success) 00104 { 00105 QObject* sender_obj = const_cast<QObject*>(sender()); 00106 RemoteService* svr = static_cast<RemoteService*>(sender_obj); 00107 disconnect(svr,SIGNAL(resolved(bool)),this,SLOT(serviceResolved(bool))); 00108 QValueList<RemoteService::Ptr>::Iterator it = d->m_duringResolve.begin(); 00109 QValueList<RemoteService::Ptr>::Iterator itEnd = d->m_duringResolve.end(); 00110 while ( it!= itEnd && svr!= (*it)) ++it; 00111 if (it != itEnd) { 00112 if (success) { 00113 d->m_services+=(*it); 00114 emit serviceAdded(svr); 00115 } 00116 d->m_duringResolve.remove(it); 00117 queryFinished(); 00118 } 00119 } 00120 00121 void ServiceBrowser::startBrowse() 00122 { 00123 if (d->m_running) return; 00124 d->m_running=true; 00125 if (isAvailable()!=Working) return; 00126 if (d->m_domains->isRunning()) { 00127 QStringList::const_iterator itEnd = d->m_domains->domains().end(); 00128 for ( QStringList::const_iterator it = d->m_domains->domains().begin(); it != itEnd; ++it ) 00129 addDomain(*it); 00130 } else d->m_domains->startBrowse(); 00131 } 00132 00133 void ServiceBrowser::gotNewService(RemoteService::Ptr svr) 00134 { 00135 if (findDuplicate(svr)==(d->m_services.end())) { 00136 if (d->m_flags & AutoResolve) { 00137 connect(svr,SIGNAL(resolved(bool )),this,SLOT(serviceResolved(bool ))); 00138 d->m_duringResolve+=svr; 00139 svr->resolveAsync(); 00140 } else { 00141 d->m_services+=svr; 00142 emit serviceAdded(svr); 00143 } 00144 } 00145 } 00146 00147 void ServiceBrowser::gotRemoveService(RemoteService::Ptr svr) 00148 { 00149 QValueList<RemoteService::Ptr>::Iterator it = findDuplicate(svr); 00150 if (it!=(d->m_services.end())) { 00151 emit serviceRemoved(*it); 00152 d->m_services.remove(it); 00153 } 00154 } 00155 00156 00157 void ServiceBrowser::removeDomain(const QString& domain) 00158 { 00159 while (d->resolvers[domain]) d->resolvers.remove(domain); 00160 QValueList<RemoteService::Ptr>::Iterator it = d->m_services.begin(); 00161 while (it!=d->m_services.end()) 00162 // use section to skip possible trailing dot 00163 if ((*it)->domain().section('.',0) == domain.section('.',0)) { 00164 emit serviceRemoved(*it); 00165 it = d->m_services.remove(it); 00166 } else ++it; 00167 } 00168 00169 void ServiceBrowser::addDomain(const QString& domain) 00170 { 00171 if (!d->m_running) return; 00172 if (!(d->resolvers[domain])) { 00173 QStringList::ConstIterator itEnd = d->m_types.end(); 00174 for (QStringList::ConstIterator it=d->m_types.begin(); it!=itEnd; ++it) { 00175 Query* b = new Query((*it),domain); 00176 connect(b,SIGNAL(serviceAdded(DNSSD::RemoteService::Ptr)),this, 00177 SLOT(gotNewService(DNSSD::RemoteService::Ptr))); 00178 connect(b,SIGNAL(serviceRemoved(DNSSD::RemoteService::Ptr )),this, 00179 SLOT(gotRemoveService(DNSSD::RemoteService::Ptr))); 00180 connect(b,SIGNAL(finished()),this,SLOT(queryFinished())); 00181 b->startQuery(); 00182 d->resolvers.insert(domain,b); 00183 } 00184 } 00185 } 00186 00187 void ServiceBrowser::queryFinished() 00188 { 00189 if (allFinished()) emit finished(); 00190 } 00191 00192 bool ServiceBrowser::allFinished() 00193 { 00194 if (d->m_duringResolve.count()) return false; 00195 bool all = true; 00196 QDictIterator<Query> it(d->resolvers); 00197 for ( ; it.current(); ++it) all&=(*it)->isFinished(); 00198 return all; 00199 } 00200 00201 const QValueList<RemoteService::Ptr>& ServiceBrowser::services() const 00202 { 00203 return d->m_services; 00204 } 00205 00206 void ServiceBrowser::virtual_hook(int, void*) 00207 {} 00208 00209 QValueList<RemoteService::Ptr>::Iterator ServiceBrowser::findDuplicate(RemoteService::Ptr src) 00210 { 00211 QValueList<RemoteService::Ptr>::Iterator itEnd = d->m_services.end(); 00212 for (QValueList<RemoteService::Ptr>::Iterator it = d->m_services.begin(); it!=itEnd; ++it) 00213 if ((src->type()==(*it)->type()) && (src->serviceName()==(*it)->serviceName()) && 00214 (src->domain() == (*it)->domain())) return it; 00215 return itEnd; 00216 } 00217 00218 00219 } 00220 00221 #include "servicebrowser.moc"