00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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;
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()) {
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
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;
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"