1/*
2 Copyright (c) 2006-2008 Tobias Koenig <tokoe@kde.org>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19
20#include "agentmanager.h"
21#include "agentmanager_p.h"
22
23#include "agenttype_p.h"
24#include "agentinstance_p.h"
25#include "dbusconnectionpool.h"
26#include "servermanager.h"
27
28#include "collection.h"
29
30#include <QtDBus/QDBusServiceWatcher>
31#include <QWidget>
32
33#include <KGlobal>
34#include <KLocale>
35#include <KLocalizedString>
36
37using namespace Akonadi;
38
39// @cond PRIVATE
40
41AgentInstance AgentManagerPrivate::createInstance(const AgentType &type)
42{
43 const QString &identifier = mManager->createAgentInstance(type.identifier());
44 if (identifier.isEmpty()) {
45 return AgentInstance();
46 }
47
48 return fillAgentInstanceLight(identifier);
49}
50
51void AgentManagerPrivate::agentTypeAdded(const QString &identifier)
52{
53 // Ignore agent types we already know about, for example because we called
54 // readAgentTypes before.
55 if (mTypes.contains(identifier)) {
56 return;
57 }
58
59 const AgentType type = fillAgentType(identifier);
60 if (type.isValid()) {
61 mTypes.insert(identifier, type);
62
63 // The Akonadi ServerManager assumes that the server is up and running as soon
64 // as it knows about at least one agent type.
65 // If we emit the typeAdded() signal here, it therefore thinks the server is
66 // running. However, the AgentManager does not know about all agent types yet,
67 // as the server might still have pending agentTypeAdded() signals, even though
68 // it internally knows all agent types already.
69 // This can cause situations where the client gets told by the ServerManager that
70 // the server is running, yet the client will encounter an error because the
71 // AgentManager doesn't know all types yet.
72 //
73 // Therefore, we read all agent types from the server here so they are known.
74 readAgentTypes();
75
76 emit mParent->typeAdded(type);
77 }
78}
79
80void AgentManagerPrivate::agentTypeRemoved(const QString &identifier)
81{
82 if (!mTypes.contains(identifier)) {
83 return;
84 }
85
86 const AgentType type = mTypes.take(identifier);
87 emit mParent->typeRemoved(type);
88}
89
90void AgentManagerPrivate::agentInstanceAdded(const QString &identifier)
91{
92 const AgentInstance instance = fillAgentInstance(identifier);
93 if (instance.isValid()) {
94
95 // It is possible that this function is called when the instance is already
96 // in our list we filled initially in the constructor.
97 // This happens when the constructor is called during Akonadi startup, when
98 // the agent processes are not fully loaded and have no D-Bus interface yet.
99 // The server-side agent manager then emits the instance added signal when
100 // the D-Bus interface for the agent comes up.
101 // In this case, we simply notify that the instance status has changed.
102 const bool newAgentInstance = !mInstances.contains(identifier);
103 if (newAgentInstance) {
104 mInstances.insert(identifier, instance);
105 emit mParent->instanceAdded(instance);
106 } else {
107 mInstances.remove(identifier);
108 mInstances.insert(identifier, instance);
109 emit mParent->instanceStatusChanged(instance);
110 }
111 }
112}
113
114void AgentManagerPrivate::agentInstanceRemoved(const QString &identifier)
115{
116 if (!mInstances.contains(identifier)) {
117 return;
118 }
119
120 const AgentInstance instance = mInstances.take(identifier);
121 emit mParent->instanceRemoved(instance);
122}
123
124void AgentManagerPrivate::agentInstanceStatusChanged(const QString &identifier, int status, const QString &msg)
125{
126 if (!mInstances.contains(identifier)) {
127 return;
128 }
129
130 AgentInstance &instance = mInstances[identifier];
131 instance.d->mStatus = status;
132 instance.d->mStatusMessage = msg;
133
134 emit mParent->instanceStatusChanged(instance);
135}
136
137void AgentManagerPrivate::agentInstanceProgressChanged(const QString &identifier, uint progress, const QString &msg)
138{
139 if (!mInstances.contains(identifier)) {
140 return;
141 }
142
143 AgentInstance &instance = mInstances[identifier];
144 instance.d->mProgress = progress;
145 if (!msg.isEmpty()) {
146 instance.d->mStatusMessage = msg;
147 }
148
149 emit mParent->instanceProgressChanged(instance);
150}
151
152void AgentManagerPrivate::agentInstanceWarning(const QString &identifier, const QString &msg)
153{
154 if (!mInstances.contains(identifier)) {
155 return;
156 }
157
158 AgentInstance &instance = mInstances[identifier];
159 emit mParent->instanceWarning(instance, msg);
160}
161
162void AgentManagerPrivate::agentInstanceError(const QString &identifier, const QString &msg)
163{
164 if (!mInstances.contains(identifier)) {
165 return;
166 }
167
168 AgentInstance &instance = mInstances[identifier];
169 emit mParent->instanceError(instance, msg);
170}
171
172void AgentManagerPrivate::agentInstanceOnlineChanged(const QString &identifier, bool state)
173{
174 if (!mInstances.contains(identifier)) {
175 return;
176 }
177
178 AgentInstance &instance = mInstances[identifier];
179 instance.d->mIsOnline = state;
180 emit mParent->instanceOnline(instance, state);
181}
182
183void AgentManagerPrivate::agentInstanceNameChanged(const QString &identifier, const QString &name)
184{
185 if (!mInstances.contains(identifier)) {
186 return;
187 }
188
189 AgentInstance &instance = mInstances[identifier];
190 instance.d->mName = name;
191
192 emit mParent->instanceNameChanged(instance);
193}
194
195void AgentManagerPrivate::readAgentTypes()
196{
197 const QDBusReply<QStringList> types = mManager->agentTypes();
198 if (types.isValid()) {
199 foreach (const QString &type, types.value()) {
200 if (!mTypes.contains(type)) {
201 agentTypeAdded(type);
202 }
203 }
204 }
205}
206
207void AgentManagerPrivate::readAgentInstances()
208{
209 const QDBusReply<QStringList> instances = mManager->agentInstances();
210 if (instances.isValid()) {
211 foreach (const QString &instance, instances.value()) {
212 if (!mInstances.contains(instance)) {
213 agentInstanceAdded(instance);
214 }
215 }
216 }
217}
218
219AgentType AgentManagerPrivate::fillAgentType(const QString &identifier) const
220{
221 AgentType type;
222 type.d->mIdentifier = identifier;
223 type.d->mName = mManager->agentName(identifier, KGlobal::locale()->language());
224 type.d->mDescription = mManager->agentComment(identifier, KGlobal::locale()->language());
225 type.d->mIconName = mManager->agentIcon(identifier);
226 type.d->mMimeTypes = mManager->agentMimeTypes(identifier);
227 type.d->mCapabilities = mManager->agentCapabilities(identifier);
228 type.d->mCustomProperties = mManager->agentCustomProperties(identifier);
229
230 return type;
231}
232
233void AgentManagerPrivate::setName(const AgentInstance &instance, const QString &name)
234{
235 mManager->setAgentInstanceName(instance.identifier(), name);
236}
237
238void AgentManagerPrivate::setOnline(const AgentInstance &instance, bool state)
239{
240 mManager->setAgentInstanceOnline(instance.identifier(), state);
241}
242
243void AgentManagerPrivate::configure(const AgentInstance &instance, QWidget *parent)
244{
245 qlonglong winId = 0;
246 if (parent) {
247 winId = (qlonglong)(parent->window()->winId());
248 }
249
250 mManager->agentInstanceConfigure(instance.identifier(), winId);
251}
252
253void AgentManagerPrivate::synchronize(const AgentInstance &instance)
254{
255 mManager->agentInstanceSynchronize(instance.identifier());
256}
257
258void AgentManagerPrivate::synchronizeCollectionTree(const AgentInstance &instance)
259{
260 mManager->agentInstanceSynchronizeCollectionTree(instance.identifier());
261}
262
263AgentInstance AgentManagerPrivate::fillAgentInstance(const QString &identifier) const
264{
265 AgentInstance instance;
266
267 const QString agentTypeIdentifier = mManager->agentInstanceType(identifier);
268 if (!mTypes.contains(agentTypeIdentifier)) {
269 return instance;
270 }
271
272 instance.d->mType = mTypes.value(agentTypeIdentifier);
273 instance.d->mIdentifier = identifier;
274 instance.d->mName = mManager->agentInstanceName(identifier);
275 instance.d->mStatus = mManager->agentInstanceStatus(identifier);
276 instance.d->mStatusMessage = mManager->agentInstanceStatusMessage(identifier);
277 instance.d->mProgress = mManager->agentInstanceProgress(identifier);
278 instance.d->mIsOnline = mManager->agentInstanceOnline(identifier);
279
280 return instance;
281}
282
283AgentInstance AgentManagerPrivate::fillAgentInstanceLight(const QString &identifier) const
284{
285 AgentInstance instance;
286
287 const QString agentTypeIdentifier = mManager->agentInstanceType(identifier);
288 Q_ASSERT_X(mTypes.contains(agentTypeIdentifier), "fillAgentInstanceLight", "Requests non-existing agent type");
289
290 instance.d->mType = mTypes.value(agentTypeIdentifier);
291 instance.d->mIdentifier = identifier;
292
293 return instance;
294}
295
296void AgentManagerPrivate::serviceOwnerChanged(const QString &, const QString &oldOwner, const QString &)
297{
298 if (oldOwner.isEmpty()) {
299 readAgentTypes();
300 readAgentInstances();
301 }
302}
303
304void AgentManagerPrivate::createDBusInterface()
305{
306 mTypes.clear();
307 mInstances.clear();
308 delete mManager;
309
310 mManager = new org::freedesktop::Akonadi::AgentManager(ServerManager::serviceName(ServerManager::Control),
311 QLatin1String("/AgentManager"),
312 DBusConnectionPool::threadConnection(), mParent);
313
314 QObject::connect(mManager, SIGNAL(agentTypeAdded(QString)),
315 mParent, SLOT(agentTypeAdded(QString)));
316 QObject::connect(mManager, SIGNAL(agentTypeRemoved(QString)),
317 mParent, SLOT(agentTypeRemoved(QString)));
318 QObject::connect(mManager, SIGNAL(agentInstanceAdded(QString)),
319 mParent, SLOT(agentInstanceAdded(QString)));
320 QObject::connect(mManager, SIGNAL(agentInstanceRemoved(QString)),
321 mParent, SLOT(agentInstanceRemoved(QString)));
322 QObject::connect(mManager, SIGNAL(agentInstanceStatusChanged(QString,int,QString)),
323 mParent, SLOT(agentInstanceStatusChanged(QString,int,QString)));
324 QObject::connect(mManager, SIGNAL(agentInstanceProgressChanged(QString,uint,QString)),
325 mParent, SLOT(agentInstanceProgressChanged(QString,uint,QString)));
326 QObject::connect(mManager, SIGNAL(agentInstanceNameChanged(QString,QString)),
327 mParent, SLOT(agentInstanceNameChanged(QString,QString)));
328 QObject::connect(mManager, SIGNAL(agentInstanceWarning(QString,QString)),
329 mParent, SLOT(agentInstanceWarning(QString,QString)));
330 QObject::connect(mManager, SIGNAL(agentInstanceError(QString,QString)),
331 mParent, SLOT(agentInstanceError(QString,QString)));
332 QObject::connect(mManager, SIGNAL(agentInstanceOnlineChanged(QString,bool)),
333 mParent, SLOT(agentInstanceOnlineChanged(QString,bool)));
334
335 if (mManager->isValid()) {
336 QDBusReply<QStringList> result = mManager->agentTypes();
337 if (result.isValid()) {
338 foreach (const QString &type, result.value()) {
339 const AgentType agentType = fillAgentType(type);
340 mTypes.insert(type, agentType);
341 }
342 }
343 result = mManager->agentInstances();
344 if (result.isValid()) {
345 foreach (const QString &instance, result.value()) {
346 const AgentInstance agentInstance = fillAgentInstance(instance);
347 mInstances.insert(instance, agentInstance);
348 }
349 }
350 } else {
351 kWarning() << "AgentManager failed to get a valid AgentManager DBus interface. Error is:" << mManager->lastError().type() << mManager->lastError().name() << mManager->lastError().message();
352 }
353}
354
355AgentManager *AgentManagerPrivate::mSelf = 0;
356
357AgentManager::AgentManager()
358 : QObject(0)
359 , d(new AgentManagerPrivate(this))
360{
361 // needed for queued connections on our signals
362 qRegisterMetaType<Akonadi::AgentType>();
363 qRegisterMetaType<Akonadi::AgentInstance>();
364
365 d->createDBusInterface();
366
367 QDBusServiceWatcher *watcher = new QDBusServiceWatcher(ServerManager::serviceName(ServerManager::Control),
368 DBusConnectionPool::threadConnection(),
369 QDBusServiceWatcher::WatchForOwnerChange, this);
370 connect(watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)),
371 this, SLOT(serviceOwnerChanged(QString,QString,QString)));
372}
373
374// @endcond
375
376AgentManager::~AgentManager()
377{
378 delete d;
379}
380
381AgentManager *AgentManager::self()
382{
383 if (!AgentManagerPrivate::mSelf) {
384 AgentManagerPrivate::mSelf = new AgentManager();
385 }
386
387 return AgentManagerPrivate::mSelf;
388}
389
390AgentType::List AgentManager::types() const
391{
392 return d->mTypes.values();
393}
394
395AgentType AgentManager::type(const QString &identifier) const
396{
397 return d->mTypes.value(identifier);
398}
399
400AgentInstance::List AgentManager::instances() const
401{
402 return d->mInstances.values();
403}
404
405AgentInstance AgentManager::instance(const QString &identifier) const
406{
407 return d->mInstances.value(identifier);
408}
409
410void AgentManager::removeInstance(const AgentInstance &instance)
411{
412 d->mManager->removeAgentInstance(instance.identifier());
413}
414
415void AgentManager::synchronizeCollection(const Collection &collection)
416{
417 synchronizeCollection(collection, false);
418}
419
420void AgentManager::synchronizeCollection(const Collection &collection, bool recursive)
421{
422 const QString resId = collection.resource();
423 Q_ASSERT(!resId.isEmpty());
424 d->mManager->agentInstanceSynchronizeCollection(resId, collection.id(), recursive);
425}
426
427#include "moc_agentmanager.cpp"
428