1 | /* This file is part of the KDE project |
2 | * |
3 | * Copyright (C) 2004, 2005 Jakub Stachowski <qbast@go2.pl> |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Library General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Library General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Library General Public License |
16 | * along with this library; see the file COPYING.LIB. If not, write to |
17 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
18 | * Boston, MA 02110-1301, USA. |
19 | */ |
20 | |
21 | #include "avahi-publicservice_p.h" |
22 | |
23 | #include <QtCore/QCoreApplication> |
24 | #include <QtCore/QStringList> |
25 | |
26 | #include "publicservice.h" |
27 | #ifdef HAVE_SYS_TYPES_H |
28 | #include <sys/types.h> |
29 | #endif |
30 | #include "servicebrowser.h" |
31 | #include "settings.h" |
32 | #include "avahi_server_interface.h" |
33 | #include "avahi_entrygroup_interface.h" |
34 | #ifndef KDE_USE_FINAL |
35 | Q_DECLARE_METATYPE(QList<QByteArray>) |
36 | #endif |
37 | |
38 | namespace DNSSD |
39 | { |
40 | |
41 | PublicService::PublicService(const QString& name, const QString& type, unsigned int port, |
42 | const QString& domain, const QStringList& subtypes) |
43 | : QObject(), ServiceBase(new PublicServicePrivate(this, name, type, domain, port)) |
44 | { |
45 | K_D; |
46 | if (domain.isNull()) d->m_domain="local." ; |
47 | d->m_subtypes=subtypes; |
48 | } |
49 | |
50 | |
51 | PublicService::~PublicService() |
52 | { |
53 | stop(); |
54 | } |
55 | |
56 | void PublicServicePrivate::tryApply() |
57 | { |
58 | if (fillEntryGroup()) commit(); |
59 | else { |
60 | m_parent->stop(); |
61 | emit m_parent->published(false); |
62 | } |
63 | } |
64 | |
65 | void PublicService::setServiceName(const QString& serviceName) |
66 | { |
67 | K_D; |
68 | d->m_serviceName = serviceName; |
69 | if (d->m_running) { |
70 | d->m_group->Reset(); |
71 | d->tryApply(); |
72 | } |
73 | } |
74 | |
75 | void PublicService::setDomain(const QString& domain) |
76 | { |
77 | K_D; |
78 | d->m_domain = domain; |
79 | if (d->m_running) { |
80 | d->m_group->Reset(); |
81 | d->tryApply(); |
82 | } |
83 | } |
84 | |
85 | |
86 | void PublicService::setType(const QString& type) |
87 | { |
88 | K_D; |
89 | d->m_type = type; |
90 | if (d->m_running) { |
91 | d->m_group->Reset(); |
92 | d->tryApply(); |
93 | } |
94 | } |
95 | |
96 | void PublicService::setSubTypes(const QStringList& subtypes) |
97 | { |
98 | K_D; |
99 | d->m_subtypes = subtypes; |
100 | if (d->m_running) { |
101 | d->m_group->Reset(); |
102 | d->tryApply(); |
103 | } |
104 | } |
105 | |
106 | QStringList PublicService::subtypes() const |
107 | { |
108 | K_D; |
109 | return d->m_subtypes; |
110 | } |
111 | |
112 | void PublicService::setPort(unsigned short port) |
113 | { |
114 | K_D; |
115 | d->m_port = port; |
116 | if (d->m_running) { |
117 | d->m_group->Reset(); |
118 | d->tryApply(); |
119 | } |
120 | } |
121 | |
122 | void PublicService::setTextData(const QMap<QString,QByteArray>& textData) |
123 | { |
124 | K_D; |
125 | d->m_textData = textData; |
126 | if (d->m_running) { |
127 | d->m_group->Reset(); |
128 | d->tryApply(); |
129 | } |
130 | } |
131 | |
132 | bool PublicService::isPublished() const |
133 | { |
134 | K_D; |
135 | return d->m_published; |
136 | } |
137 | |
138 | bool PublicService::publish() |
139 | { |
140 | K_D; |
141 | publishAsync(); |
142 | while (d->m_running && !d->m_published) QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); |
143 | return d->m_published; |
144 | } |
145 | |
146 | void PublicService::stop() |
147 | { |
148 | K_D; |
149 | if (d->m_group) d->m_group->Reset(); |
150 | d->m_running = false; |
151 | d->m_published = false; |
152 | } |
153 | bool PublicServicePrivate::fillEntryGroup() |
154 | { |
155 | registerTypes(); |
156 | if (!m_group) { |
157 | QDBusReply<QDBusObjectPath> rep=m_server->EntryGroupNew(); |
158 | if (!rep.isValid()) return false; |
159 | m_group=new org::freedesktop::Avahi::EntryGroup("org.freedesktop.Avahi" ,rep.value().path(), QDBusConnection::systemBus()); |
160 | connect(m_group,SIGNAL(StateChanged(int,QString)), this, SLOT(groupStateChanged(int,QString))); |
161 | } |
162 | if (m_serviceName.isNull()) { |
163 | QDBusReply<QString> rep=m_server->GetHostName(); |
164 | if (!rep.isValid()) return false; |
165 | m_serviceName=rep.value(); |
166 | } |
167 | |
168 | QList<QByteArray> txt; |
169 | QMap<QString,QByteArray>::ConstIterator itEnd = m_textData.constEnd(); |
170 | for (QMap<QString,QByteArray>::ConstIterator it = m_textData.constBegin(); it!=itEnd ; ++it) |
171 | if (it.value().isNull()) txt.append(it.key().toLatin1()); |
172 | else txt.append(it.key().toLatin1()+'='+it.value()); |
173 | |
174 | |
175 | for (;;) { |
176 | QDBusReply<void> ret = m_group->AddService(-1,-1, 0, m_serviceName, m_type , domainToDNS(m_domain) , |
177 | m_hostName, m_port,txt); |
178 | if (ret.isValid()) break; |
179 | |
180 | // serious error, bail out |
181 | if (ret.error().name()!=QLatin1String("org.freedesktop.Avahi.CollisionError" )) return false; |
182 | |
183 | // name collision, try another |
184 | QDBusReply<QString> rep=m_server->GetAlternativeServiceName(m_serviceName); |
185 | if (rep.isValid()) m_serviceName = rep.value(); |
186 | else return false; |
187 | } |
188 | |
189 | Q_FOREACH(const QString &subtype, m_subtypes) |
190 | m_group->AddServiceSubtype(-1,-1, 0, m_serviceName, m_type, domainToDNS(m_domain) , subtype); |
191 | return true; |
192 | } |
193 | |
194 | void PublicServicePrivate::serverStateChanged(int s,const QString&) |
195 | { |
196 | if (!m_running) return; |
197 | switch (s) { |
198 | case AVAHI_SERVER_INVALID: |
199 | m_parent->stop(); |
200 | emit m_parent->published(false); |
201 | break; |
202 | case AVAHI_SERVER_REGISTERING: |
203 | case AVAHI_SERVER_COLLISION: |
204 | if (m_group) m_group->Reset(); |
205 | m_collision=true; |
206 | break; |
207 | case AVAHI_SERVER_RUNNING: |
208 | if (m_collision) { |
209 | m_collision=false; |
210 | tryApply(); |
211 | } |
212 | } |
213 | } |
214 | |
215 | void PublicService::publishAsync() |
216 | { |
217 | K_D; |
218 | if (d->m_running) stop(); |
219 | |
220 | if (!d->m_server) { |
221 | d->m_server = new org::freedesktop::Avahi::Server("org.freedesktop.Avahi" ,"/" ,QDBusConnection::systemBus()); |
222 | connect(d->m_server,SIGNAL(StateChanged(int,QString)),d,SLOT(serverStateChanged(int,QString))); |
223 | } |
224 | |
225 | int state=AVAHI_SERVER_INVALID; |
226 | QDBusReply<int> rep=d->m_server->GetState(); |
227 | |
228 | if (rep.isValid()) state=rep.value(); |
229 | d->m_running=true; |
230 | d->m_collision=true; // make it look like server is getting out of collision to force registering |
231 | d->serverStateChanged(state, QString()); |
232 | } |
233 | |
234 | |
235 | void PublicServicePrivate::groupStateChanged(int s, const QString& reason) |
236 | { |
237 | switch (s) { |
238 | case AVAHI_ENTRY_GROUP_COLLISION: { |
239 | QDBusReply<QString> rep=m_server->GetAlternativeServiceName(m_serviceName); |
240 | if (rep.isValid()) m_parent->setServiceName(rep.value()); |
241 | else serverStateChanged(AVAHI_SERVER_INVALID, reason); |
242 | break; |
243 | } |
244 | case AVAHI_ENTRY_GROUP_ESTABLISHED: |
245 | m_published=true; |
246 | emit m_parent->published(true); |
247 | break; |
248 | case AVAHI_ENTRY_GROUP_FAILURE: |
249 | serverStateChanged(AVAHI_SERVER_INVALID, reason); |
250 | default: |
251 | break; |
252 | } |
253 | } |
254 | |
255 | void PublicService::virtual_hook(int, void*) |
256 | { |
257 | } |
258 | |
259 | |
260 | } |
261 | |
262 | #include "publicservice.moc" |
263 | #include "avahi-publicservice_p.moc" |
264 | |