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