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 <qapplication.h> 00030 #include <network/ksocketaddress.h> 00031 #include <kurl.h> 00032 #include <unistd.h> 00033 #include "sdevent.h" 00034 #include "responder.h" 00035 #include "servicebrowser.h" 00036 #include "settings.h" 00037 00038 namespace DNSSD 00039 { 00040 static unsigned long publicIP(); 00041 #ifdef HAVE_DNSSD 00042 void publish_callback (DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, 00043 const char*, const char*, void *context); 00044 #endif 00045 class PublicServicePrivate : public Responder 00046 { 00047 public: 00048 PublicServicePrivate() : m_published(false) 00049 {} 00050 bool m_published; 00051 }; 00052 00053 PublicService::PublicService(const QString& name, const QString& type, unsigned int port, 00054 const QString& domain) 00055 : QObject(), ServiceBase(name, type, QString::null, domain, port) 00056 { 00057 d = new PublicServicePrivate; 00058 if (domain.isNull()) 00059 if (Configuration::publishType()==Configuration::EnumPublishType::LAN) m_domain="local."; 00060 else m_domain=Configuration::publishDomain(); 00061 } 00062 00063 00064 PublicService::~PublicService() 00065 { 00066 stop(); 00067 delete d; 00068 } 00069 00070 void PublicService::setServiceName(const QString& serviceName) 00071 { 00072 m_serviceName = serviceName; 00073 if (d->isRunning()) { 00074 stop(); 00075 publishAsync(); 00076 } 00077 } 00078 00079 void PublicService::setDomain(const QString& domain) 00080 { 00081 m_domain = domain; 00082 if (d->isRunning()) { 00083 stop(); 00084 publishAsync(); 00085 } 00086 } 00087 00088 00089 void PublicService::setType(const QString& type) 00090 { 00091 m_type = type; 00092 if (d->isRunning()) { 00093 stop(); 00094 publishAsync(); 00095 } 00096 } 00097 00098 void PublicService::setPort(unsigned short port) 00099 { 00100 m_port = port; 00101 if (d->isRunning()) { 00102 stop(); 00103 publishAsync(); 00104 } 00105 } 00106 00107 bool PublicService::isPublished() const 00108 { 00109 return d->m_published; 00110 } 00111 00112 void PublicService::setTextData(const QMap<QString,QString>& textData) 00113 { 00114 m_textData = textData; 00115 if (d->isRunning()) { 00116 stop(); 00117 publishAsync(); 00118 } 00119 } 00120 00121 bool PublicService::publish() 00122 { 00123 publishAsync(); 00124 while (d->isRunning() && !d->m_published) d->process(); 00125 return d->m_published; 00126 } 00127 00128 void PublicService::stop() 00129 { 00130 d->stop(); 00131 d->m_published = false; 00132 } 00133 00134 void PublicService::publishAsync() 00135 { 00136 if (d->isRunning()) stop(); 00137 #ifdef HAVE_DNSSD 00138 if (ServiceBrowser::isAvailable()==ServiceBrowser::Working) { 00139 TXTRecordRef txt; 00140 TXTRecordCreate(&txt,0,0); 00141 QMap<QString,QString>::ConstIterator itEnd = m_textData.end(); 00142 for (QMap<QString,QString>::ConstIterator it = m_textData.begin(); it!=itEnd ; ++it) { 00143 QCString value = it.data().utf8(); 00144 if (TXTRecordSetValue(&txt,it.key().utf8(),value.length(),value)!=kDNSServiceErr_NoError) { 00145 TXTRecordDeallocate(&txt); 00146 emit published(false); 00147 return; 00148 } 00149 } 00150 DNSServiceRef ref; 00151 if (DNSServiceRegister(&ref,0,0,m_serviceName.utf8(),m_type.ascii(),domainToDNS(m_domain),NULL, 00152 htons(m_port),TXTRecordGetLength(&txt),TXTRecordGetBytesPtr(&txt),publish_callback, 00153 reinterpret_cast<void*>(this)) == kDNSServiceErr_NoError) d->setRef(ref); 00154 TXTRecordDeallocate(&txt); 00155 } 00156 #endif 00157 if (!d->isRunning()) emit published(false); 00158 } 00159 00160 #ifdef HAVE_DNSSD 00161 void publish_callback (DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, 00162 const char*, const char*, void *context) 00163 { 00164 QObject *obj = reinterpret_cast<QObject*>(context); 00165 if (errorCode != kDNSServiceErr_NoError) { 00166 ErrorEvent err; 00167 QApplication::sendEvent(obj, &err); 00168 } else { 00169 PublishEvent pev(QString::fromUtf8(name)); 00170 QApplication::sendEvent(obj, &pev); 00171 } 00172 } 00173 #endif 00174 00175 const KURL PublicService::toInvitation(const QString& host) 00176 { 00177 KURL url; 00178 url.setProtocol("invitation"); 00179 if (host.isEmpty()) { // select best address 00180 unsigned long s_address = publicIP(); 00181 if (!s_address) return KURL(); 00182 KNetwork::KIpAddress addr(s_address); 00183 url.setHost(addr.toString()); 00184 } else url.setHost(host); 00185 //FIXME: if there is no public interface, select any non-loopback 00186 url.setPort(m_port); 00187 url.setPath("/"+m_type+"/"+KURL::encode_string(m_serviceName)); 00188 QString query; 00189 QMap<QString,QString>::ConstIterator itEnd = m_textData.end(); 00190 for (QMap<QString,QString>::ConstIterator it = m_textData.begin(); it!=itEnd ; ++it) 00191 url.addQueryItem(it.key(),it.data());; 00192 return url; 00193 } 00194 00195 void PublicService::customEvent(QCustomEvent* event) 00196 { 00197 if (event->type()==QEvent::User+SD_ERROR) { 00198 stop(); 00199 emit published(false); 00200 } 00201 if (event->type()==QEvent::User+SD_PUBLISH) { 00202 d->m_published=true; 00203 emit published(true); 00204 m_serviceName = static_cast<PublishEvent*>(event)->m_name; 00205 } 00206 } 00207 00208 void PublicService::virtual_hook(int, void*) 00209 { 00210 } 00211 00212 static unsigned long publicIP() 00213 { 00214 struct sockaddr_in addr; 00215 socklen_t len = sizeof(addr); 00216 int sock = socket(AF_INET,SOCK_DGRAM,0); 00217 if (sock == -1) return 0; 00218 addr.sin_family = AF_INET; 00219 addr.sin_port = 1; // Not important, any port and public address will do 00220 addr.sin_addr.s_addr = 0x11111111; 00221 if ((connect(sock,(const struct sockaddr*)&addr,sizeof(addr))) == -1) { close(sock); return 0; } 00222 if ((getsockname(sock,(struct sockaddr*)&addr, &len)) == -1) { close(sock); return 0; } 00223 ::close(sock); 00224 return addr.sin_addr.s_addr; 00225 } 00226 00227 00228 } 00229 00230 #include "publicservice.moc"