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