publicservice.cpp
00001 /* This file is part of the KDE project 00002 * 00003 * Copyright (C) 2004, 2005 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 "config.h" 00022 00023 #include "publicservice.h" 00024 #ifdef HAVE_SYS_TYPES_H 00025 #include <sys/types.h> 00026 #endif 00027 #include <netinet/in.h> 00028 #include <sys/socket.h> 00029 #include <tqapplication.h> 00030 #include <network/tdesocketaddress.h> 00031 #include <kurl.h> 00032 #include <unistd.h> 00033 #ifdef HAVE_DNSSD 00034 #include <avahi-client/client.h> 00035 #ifdef AVAHI_API_0_6 00036 #include <avahi-client/publish.h> 00037 #endif 00038 #include <avahi-common/alternative.h> 00039 #include <avahi-common/strlst.h> 00040 #endif 00041 #include "sdevent.h" 00042 #include "responder.h" 00043 #include "servicebrowser.h" 00044 #include "settings.h" 00045 00046 namespace DNSSD 00047 { 00048 static unsigned long publicIP(); 00049 00050 #ifdef HAVE_DNSSD 00051 void publish_callback (AvahiEntryGroup*, AvahiEntryGroupState s, void *context); 00052 #endif 00053 00054 class PublicServicePrivate 00055 { 00056 public: 00057 PublicServicePrivate() : m_published(false), m_running(false), m_collision(false) 00058 #ifdef HAVE_DNSSD 00059 , m_group(0) 00060 #endif 00061 {} 00062 bool m_published; 00063 bool m_running; 00064 bool m_collision; 00065 #ifdef HAVE_DNSSD 00066 AvahiEntryGroup* m_group; 00067 #endif 00068 void commit() 00069 { 00070 #ifdef HAVE_DNSSD 00071 if (!m_collision) avahi_entry_group_commit(m_group); 00072 #endif 00073 } 00074 00075 }; 00076 00077 PublicService::PublicService(const TQString& name, const TQString& type, unsigned int port, 00078 const TQString& domain) 00079 : TQObject(), ServiceBase(name, type, TQString::null, domain, port) 00080 { 00081 d = new PublicServicePrivate; 00082 #ifdef HAVE_DNSSD 00083 if (Responder::self().client()) { 00084 d->m_group = avahi_entry_group_new(Responder::self().client(), publish_callback,this); 00085 connect(&Responder::self(),TQT_SIGNAL(stateChanged(AvahiClientState)),this,TQT_SLOT(clientState(AvahiClientState))); 00086 } 00087 #endif 00088 if (domain.isNull()) 00089 if (Configuration::publishType()==Configuration::EnumPublishType::LAN) m_domain="local."; 00090 else m_domain=Configuration::publishDomain(); 00091 } 00092 00093 00094 PublicService::~PublicService() 00095 { 00096 #ifdef HAVE_DNSSD 00097 if (d->m_group) avahi_entry_group_free(d->m_group); 00098 #endif 00099 delete d; 00100 } 00101 00102 void PublicService::tryApply() 00103 { 00104 if (fillEntryGroup()) d->commit(); 00105 else { 00106 stop(); 00107 emit published(false); 00108 } 00109 } 00110 00111 void PublicService::setServiceName(const TQString& serviceName) 00112 { 00113 m_serviceName = serviceName; 00114 #ifdef HAVE_DNSSD 00115 if (d->m_running) { 00116 avahi_entry_group_reset(d->m_group); 00117 tryApply(); 00118 } 00119 #endif 00120 } 00121 00122 void PublicService::setDomain(const TQString& domain) 00123 { 00124 m_domain = domain; 00125 #ifdef HAVE_DNSSD 00126 if (d->m_running) { 00127 avahi_entry_group_reset(d->m_group); 00128 tryApply(); 00129 } 00130 #endif 00131 } 00132 00133 00134 void PublicService::setType(const TQString& type) 00135 { 00136 m_type = type; 00137 #ifdef HAVE_DNSSD 00138 if (d->m_running) { 00139 avahi_entry_group_reset(d->m_group); 00140 tryApply(); 00141 } 00142 #endif 00143 } 00144 00145 void PublicService::setPort(unsigned short port) 00146 { 00147 m_port = port; 00148 #ifdef HAVE_DNSSD 00149 if (d->m_running) { 00150 avahi_entry_group_reset(d->m_group); 00151 tryApply(); 00152 } 00153 #endif 00154 } 00155 00156 void PublicService::setTextData(const TQMap<TQString,TQString>& textData) 00157 { 00158 m_textData = textData; 00159 #ifdef HAVE_DNSSD 00160 if (d->m_running) { 00161 avahi_entry_group_reset(d->m_group); 00162 tryApply(); 00163 } 00164 #endif 00165 } 00166 00167 bool PublicService::isPublished() const 00168 { 00169 return d->m_published; 00170 } 00171 00172 bool PublicService::publish() 00173 { 00174 publishAsync(); 00175 while (d->m_running && !d->m_published) Responder::self().process(); 00176 return d->m_published; 00177 } 00178 00179 void PublicService::stop() 00180 { 00181 #ifdef HAVE_DNSSD 00182 if (d->m_group) avahi_entry_group_reset(d->m_group); 00183 #endif 00184 d->m_published = false; 00185 } 00186 bool PublicService::fillEntryGroup() 00187 { 00188 #ifdef HAVE_DNSSD 00189 AvahiStringList *s=0; 00190 TQMap<TQString,TQString>::ConstIterator itEnd = m_textData.end(); 00191 for (TQMap<TQString,TQString>::ConstIterator it = m_textData.begin(); it!=itEnd ; ++it) 00192 s = avahi_string_list_add_pair(s, it.key().utf8(),it.data().utf8()); 00193 #ifdef AVAHI_API_0_6 00194 bool res = (!avahi_entry_group_add_service_strlst(d->m_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, (AvahiPublishFlags)0, 00195 m_serviceName.isNull() ? avahi_client_get_host_name(Responder::self().client()) : m_serviceName.utf8().data(), 00196 m_type.ascii(),domainToDNS(m_domain),m_hostName.utf8(),m_port,s)); 00197 #else 00198 bool res = (!avahi_entry_group_add_service_strlst(d->m_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 00199 m_serviceName.isNull() ? avahi_client_get_host_name(Responder::self().client()) : m_serviceName.utf8().data(), 00200 m_type.ascii(),m_domain.utf8(),m_hostName.utf8(),m_port,s)); 00201 #endif 00202 avahi_string_list_free(s); 00203 return res; 00204 #else 00205 return FALSE; 00206 #endif 00207 } 00208 00209 void PublicService::clientState(AvahiClientState s) 00210 { 00211 if (!d->m_running) return; 00212 #ifdef HAVE_DNSSD 00213 switch (s) { 00214 #ifdef AVAHI_API_0_6 00215 case AVAHI_CLIENT_FAILURE: 00216 #else 00217 case AVAHI_CLIENT_S_INVALID: 00218 case AVAHI_CLIENT_DISCONNECTED: 00219 #endif 00220 stop(); 00221 emit published(false); 00222 break; 00223 case AVAHI_CLIENT_S_REGISTERING: 00224 case AVAHI_CLIENT_S_COLLISION: 00225 avahi_entry_group_reset(d->m_group); 00226 d->m_collision=true; 00227 break; 00228 case AVAHI_CLIENT_S_RUNNING: 00229 if (d->m_collision) { 00230 d->m_collision=false; 00231 tryApply(); 00232 } 00233 } 00234 #endif 00235 } 00236 00237 void PublicService::publishAsync() 00238 { 00239 if (d->m_running) stop(); 00240 00241 #ifdef HAVE_DNSSD 00242 if (!d->m_group) { 00243 emit published(false); 00244 return; 00245 } 00246 AvahiClientState s=Responder::self().state(); 00247 #endif 00248 d->m_running=true; 00249 d->m_collision=true; // make it look like server is getting out of collision to force registering 00250 #ifdef HAVE_DNSSD 00251 clientState(s); 00252 #endif 00253 } 00254 00255 #ifdef HAVE_DNSSD 00256 void publish_callback (AvahiEntryGroup*, AvahiEntryGroupState s, void *context) 00257 { 00258 TQObject *obj = reinterpret_cast<TQObject*>(context); 00259 if (s!=AVAHI_ENTRY_GROUP_ESTABLISHED && s!=AVAHI_ENTRY_GROUP_COLLISION) return; 00260 PublishEvent* pev=new PublishEvent(s==AVAHI_ENTRY_GROUP_ESTABLISHED); 00261 TQApplication::postEvent(obj, pev); 00262 } 00263 #endif 00264 00265 const KURL PublicService::toInvitation(const TQString& host) 00266 { 00267 KURL url; 00268 url.setProtocol("invitation"); 00269 if (host.isEmpty()) { // select best address 00270 unsigned long s_address = publicIP(); 00271 if (!s_address) return KURL(); 00272 KNetwork::KIpAddress addr(s_address); 00273 url.setHost(addr.toString()); 00274 } else url.setHost(host); 00275 //FIXME: if there is no public interface, select any non-loopback 00276 url.setPort(m_port); 00277 url.setPath("/"+m_type+"/"+KURL::encode_string(m_serviceName)); 00278 TQString query; 00279 TQMap<TQString,TQString>::ConstIterator itEnd = m_textData.end(); 00280 for (TQMap<TQString,TQString>::ConstIterator it = m_textData.begin(); it!=itEnd ; ++it) 00281 url.addQueryItem(it.key(),it.data());; 00282 return url; 00283 } 00284 00285 void PublicService::customEvent(TQCustomEvent* event) 00286 { 00287 #ifdef HAVE_DNSSD 00288 if (event->type()==TQEvent::User+SD_PUBLISH) { 00289 if (!static_cast<PublishEvent*>(event)->m_ok) { 00290 setServiceName(TQString::fromUtf8(avahi_alternative_service_name(m_serviceName.utf8()))); 00291 return; 00292 } 00293 d->m_published=true; 00294 emit published(true); 00295 } 00296 #endif 00297 } 00298 00299 void PublicService::virtual_hook(int, void*) 00300 { 00301 } 00302 00303 static unsigned long publicIP() 00304 { 00305 struct sockaddr_in addr; 00306 socklen_t len = sizeof(addr); 00307 int sock = socket(AF_INET,SOCK_DGRAM,0); 00308 if (sock == -1) return 0; 00309 addr.sin_family = AF_INET; 00310 addr.sin_port = 1; // Not important, any port and public address will do 00311 addr.sin_addr.s_addr = 0x11111111; 00312 if ((connect(sock,(const struct sockaddr*)&addr,sizeof(addr))) == -1) { close(sock); return 0; } 00313 if ((getsockname(sock,(struct sockaddr*)&addr, &len)) == -1) { close(sock); return 0; } 00314 ::close(sock); 00315 return addr.sin_addr.s_addr; 00316 } 00317 00318 00319 } 00320 00321 #include "publicservice.moc"