Akonadi

core/agentmanager.cpp
1 /*
2  Copyright (c) 2006-2008 Tobias Koenig <[email protected]>
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 <QDBusConnection>
26 #include "servermanager.h"
27 #include "collection.h"
28 
29 #include "shared/akranges.h"
30 
31 #include <QDBusServiceWatcher>
32 #include <QWidget>
33 
34 using namespace Akonadi;
35 using namespace AkRanges;
36 
37 // @cond PRIVATE
38 
39 AgentInstance AgentManagerPrivate::createInstance(const AgentType &type)
40 {
41  const QString &identifier = mManager->createAgentInstance(type.identifier());
42  if (identifier.isEmpty()) {
43  return AgentInstance();
44  }
45 
46  return fillAgentInstanceLight(identifier);
47 }
48 
49 void AgentManagerPrivate::agentTypeAdded(const QString &identifier)
50 {
51  // Ignore agent types we already know about, for example because we called
52  // readAgentTypes before.
53  if (mTypes.contains(identifier)) {
54  return;
55  }
56 
57  if (mTypes.isEmpty()) {
58  // The Akonadi ServerManager assumes that the server is up and running as soon
59  // as it knows about at least one agent type.
60  // If we Q_EMIT the typeAdded() signal here, it therefore thinks the server is
61  // running. However, the AgentManager does not know about all agent types yet,
62  // as the server might still have pending agentTypeAdded() signals, even though
63  // it internally knows all agent types already.
64  // This can cause situations where the client gets told by the ServerManager that
65  // the server is running, yet the client will encounter an error because the
66  // AgentManager doesn't know all types yet.
67  //
68  // Therefore, we read all agent types from the server here so they are known.
69  readAgentTypes();
70  }
71 
72  const AgentType type = fillAgentType(identifier);
73  if (type.isValid()) {
74  mTypes.insert(identifier, type);
75 
76  Q_EMIT mParent->typeAdded(type);
77  }
78 }
79 
80 void AgentManagerPrivate::agentTypeRemoved(const QString &identifier)
81 {
82  if (!mTypes.contains(identifier)) {
83  return;
84  }
85 
86  const AgentType type = mTypes.take(identifier);
87  Q_EMIT mParent->typeRemoved(type);
88 }
89 
90 void 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  Q_EMIT mParent->instanceAdded(instance);
106  } else {
107  mInstances.remove(identifier);
108  mInstances.insert(identifier, instance);
109  Q_EMIT mParent->instanceStatusChanged(instance);
110  }
111  }
112 }
113 
114 void AgentManagerPrivate::agentInstanceRemoved(const QString &identifier)
115 {
116  if (!mInstances.contains(identifier)) {
117  return;
118  }
119 
120  const AgentInstance instance = mInstances.take(identifier);
121  Q_EMIT mParent->instanceRemoved(instance);
122 }
123 
124 void 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  Q_EMIT mParent->instanceStatusChanged(instance);
135 }
136 
137 void 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  Q_EMIT mParent->instanceProgressChanged(instance);
150 }
151 
152 void AgentManagerPrivate::agentInstanceWarning(const QString &identifier, const QString &msg)
153 {
154  if (!mInstances.contains(identifier)) {
155  return;
156  }
157 
158  AgentInstance &instance = mInstances[identifier];
159  Q_EMIT mParent->instanceWarning(instance, msg);
160 }
161 
162 void AgentManagerPrivate::agentInstanceError(const QString &identifier, const QString &msg)
163 {
164  if (!mInstances.contains(identifier)) {
165  return;
166  }
167 
168  AgentInstance &instance = mInstances[identifier];
169  Q_EMIT mParent->instanceError(instance, msg);
170 }
171 
172 void 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  Q_EMIT mParent->instanceOnline(instance, state);
181 }
182 
183 void 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  Q_EMIT mParent->instanceNameChanged(instance);
193 }
194 
195 void AgentManagerPrivate::readAgentTypes()
196 {
197  const QDBusReply<QStringList> types = mManager->agentTypes();
198  if (types.isValid()) {
199  const QStringList lst = types.value();
200  for (const QString &type : lst) {
201  const AgentType agentType = fillAgentType(type);
202  if (agentType.isValid()) {
203  mTypes.insert(type, agentType);
204  Q_EMIT mParent->typeAdded(agentType);
205  }
206  }
207  }
208 }
209 
210 void AgentManagerPrivate::readAgentInstances()
211 {
212  const QDBusReply<QStringList> instances = mManager->agentInstances();
213  if (instances.isValid()) {
214  const QStringList lst = instances.value();
215  for (const QString &instance : lst) {
216  const AgentInstance agentInstance = fillAgentInstance(instance);
217  if (agentInstance.isValid()) {
218  mInstances.insert(instance, agentInstance);
219  Q_EMIT mParent->instanceAdded(agentInstance);
220  }
221  }
222  }
223 }
224 
225 AgentType AgentManagerPrivate::fillAgentType(const QString &identifier) const
226 {
227  AgentType type;
228  type.d->mIdentifier = identifier;
229  type.d->mName = mManager->agentName(identifier);
230  type.d->mDescription = mManager->agentComment(identifier);
231  type.d->mIconName = mManager->agentIcon(identifier);
232  type.d->mMimeTypes = mManager->agentMimeTypes(identifier);
233  type.d->mCapabilities = mManager->agentCapabilities(identifier);
234  type.d->mCustomProperties = mManager->agentCustomProperties(identifier);
235 
236  return type;
237 }
238 
239 void AgentManagerPrivate::setName(const AgentInstance &instance, const QString &name)
240 {
241  mManager->setAgentInstanceName(instance.identifier(), name);
242 }
243 
244 void AgentManagerPrivate::setOnline(const AgentInstance &instance, bool state)
245 {
246  mManager->setAgentInstanceOnline(instance.identifier(), state);
247 }
248 
249 void AgentManagerPrivate::configure(const AgentInstance &instance, QWidget *parent)
250 {
251  qlonglong winId = 0;
252  if (parent) {
253  winId = static_cast<qlonglong>(parent->window()->winId());
254  }
255 
256  mManager->agentInstanceConfigure(instance.identifier(), winId);
257 }
258 
259 void AgentManagerPrivate::synchronize(const AgentInstance &instance)
260 {
261  mManager->agentInstanceSynchronize(instance.identifier());
262 }
263 
264 void AgentManagerPrivate::synchronizeCollectionTree(const AgentInstance &instance)
265 {
266  mManager->agentInstanceSynchronizeCollectionTree(instance.identifier());
267 }
268 
269 void AgentManagerPrivate::synchronizeTags(const AgentInstance &instance)
270 {
271  mManager->agentInstanceSynchronizeTags(instance.identifier());
272 }
273 
274 void AgentManagerPrivate::synchronizeRelations(const AgentInstance &instance)
275 {
276  mManager->agentInstanceSynchronizeRelations(instance.identifier());
277 }
278 
279 AgentInstance AgentManagerPrivate::fillAgentInstance(const QString &identifier) const
280 {
281  AgentInstance instance;
282 
283  const QString agentTypeIdentifier = mManager->agentInstanceType(identifier);
284  if (!mTypes.contains(agentTypeIdentifier)) {
285  return instance;
286  }
287 
288  instance.d->mType = mTypes.value(agentTypeIdentifier);
289  instance.d->mIdentifier = identifier;
290  instance.d->mName = mManager->agentInstanceName(identifier);
291  instance.d->mStatus = mManager->agentInstanceStatus(identifier);
292  instance.d->mStatusMessage = mManager->agentInstanceStatusMessage(identifier);
293  instance.d->mProgress = mManager->agentInstanceProgress(identifier);
294  instance.d->mIsOnline = mManager->agentInstanceOnline(identifier);
295 
296  return instance;
297 }
298 
299 AgentInstance AgentManagerPrivate::fillAgentInstanceLight(const QString &identifier) const
300 {
301  AgentInstance instance;
302 
303  const QString agentTypeIdentifier = mManager->agentInstanceType(identifier);
304  Q_ASSERT_X(mTypes.contains(agentTypeIdentifier), "fillAgentInstanceLight", "Requests non-existing agent type");
305 
306  instance.d->mType = mTypes.value(agentTypeIdentifier);
307  instance.d->mIdentifier = identifier;
308 
309  return instance;
310 }
311 
312 void AgentManagerPrivate::createDBusInterface()
313 {
314  mTypes.clear();
315  mInstances.clear();
316  delete mManager;
317 
318  mManager = new org::freedesktop::Akonadi::AgentManager(ServerManager::serviceName(ServerManager::Control),
319  QStringLiteral("/AgentManager"),
320  QDBusConnection::sessionBus(), mParent);
321 
322  QObject::connect(mManager, SIGNAL(agentTypeAdded(QString)),
323  mParent, SLOT(agentTypeAdded(QString)));
324  QObject::connect(mManager, SIGNAL(agentTypeRemoved(QString)),
325  mParent, SLOT(agentTypeRemoved(QString)));
326  QObject::connect(mManager, SIGNAL(agentInstanceAdded(QString)),
327  mParent, SLOT(agentInstanceAdded(QString)));
328  QObject::connect(mManager, SIGNAL(agentInstanceRemoved(QString)),
329  mParent, SLOT(agentInstanceRemoved(QString)));
330  QObject::connect(mManager, SIGNAL(agentInstanceStatusChanged(QString,int,QString)),
331  mParent, SLOT(agentInstanceStatusChanged(QString,int,QString)));
332  QObject::connect(mManager, SIGNAL(agentInstanceProgressChanged(QString,uint,QString)),
333  mParent, SLOT(agentInstanceProgressChanged(QString,uint,QString)));
334  QObject::connect(mManager, SIGNAL(agentInstanceNameChanged(QString,QString)),
335  mParent, SLOT(agentInstanceNameChanged(QString,QString)));
336  QObject::connect(mManager, SIGNAL(agentInstanceWarning(QString,QString)),
337  mParent, SLOT(agentInstanceWarning(QString,QString)));
338  QObject::connect(mManager, SIGNAL(agentInstanceError(QString,QString)),
339  mParent, SLOT(agentInstanceError(QString,QString)));
340  QObject::connect(mManager, SIGNAL(agentInstanceOnlineChanged(QString,bool)),
341  mParent, SLOT(agentInstanceOnlineChanged(QString,bool)));
342 
343  if (mManager->isValid()) {
344  readAgentTypes();
345  readAgentInstances();
346  }
347 }
348 
349 AgentManager *AgentManagerPrivate::mSelf = nullptr;
350 
352  : QObject(nullptr)
353  , d(new AgentManagerPrivate(this))
354 {
355  // needed for queued connections on our signals
356  qRegisterMetaType<Akonadi::AgentType>();
357  qRegisterMetaType<Akonadi::AgentInstance>();
358 
359  d->createDBusInterface();
360 
361  d->mServiceWatcher = std::make_unique<QDBusServiceWatcher>(
362  ServerManager::serviceName(ServerManager::Control), QDBusConnection::sessionBus(),
363  QDBusServiceWatcher::WatchForRegistration);
364  connect(d->mServiceWatcher.get(), &QDBusServiceWatcher::serviceRegistered,
365  this, [this]() {
366  if (d->mTypes.isEmpty()) { // just to be safe
367  d->readAgentTypes();
368  }
369  if (d->mInstances.isEmpty()) {
370  d->readAgentInstances();
371  }
372  });
373 }
374 
375 // @endcond
376 
378 {
379  delete d;
380 }
381 
382 AgentManager *AgentManager::self()
383 {
384  if (!AgentManagerPrivate::mSelf) {
385  AgentManagerPrivate::mSelf = new AgentManager();
386  }
387 
388  return AgentManagerPrivate::mSelf;
389 }
390 
391 AgentType::List AgentManager::types() const
392 {
393  // Maybe the Control process is up and ready but we haven't been to the event loop yet so
394  // QDBusServiceWatcher hasn't notified us yet.
395  // In that case make sure to do it here, to avoid going into Broken state.
396  if (d->mTypes.isEmpty()) {
397  d->readAgentTypes();
398  }
399  return d->mTypes | Views::values | Actions::toQVector;
400 }
401 
402 AgentType AgentManager::type(const QString &identifier) const
403 {
404  return d->mTypes.value(identifier);
405 }
406 
407 AgentInstance::List AgentManager::instances() const
408 {
409  return d->mInstances | Views::values | Actions::toQVector;
410 }
411 
412 AgentInstance AgentManager::instance(const QString &identifier) const
413 {
414  return d->mInstances.value(identifier);
415 }
416 
417 void AgentManager::removeInstance(const AgentInstance &instance)
418 {
419  d->mManager->removeAgentInstance(instance.identifier());
420 }
421 
422 void AgentManager::synchronizeCollection(const Collection &collection)
423 {
424  synchronizeCollection(collection, false);
425 }
426 
427 void AgentManager::synchronizeCollection(const Collection &collection, bool recursive)
428 {
429  const QString resId = collection.resource();
430  Q_ASSERT(!resId.isEmpty());
431  d->mManager->agentInstanceSynchronizeCollection(resId, collection.id(), recursive);
432 }
433 
434 #include "moc_agentmanager.cpp"
void serviceRegistered(const QString &serviceName)
Provides an interface to retrieve agent types and manage agent instances.
QWidget * window() const const
AgentManager(bool verbose, QObject *parent=nullptr)
Creates a new agent manager.
~AgentManager()
Destroys the agent manager.
Represents a collection of PIM items.
Definition: collection.h:76
bool isValid() const
Returns whether the agent type is valid.
bool isValid() const const
QString identifier() const
Returns the unique identifier of the agent instance.
QDBusConnection sessionBus()
static QString serviceName(ServiceType serviceType)
Returns the namespaced D-Bus service name for serviceType.
QString identifier() const
Returns the unique identifier of the agent type.
A representation of an agent type.
QDBusReply::Type value() const const
bool isValid() const
Returns whether the agent instance object is valid.
bool isEmpty() const const
QString resource() const
Returns the identifier of the resource owning the collection.
Definition: collection.cpp:318
WId winId() const const
QStringList types(Mode mode=Writing)
Id id() const
Returns the unique identifier of the collection.
Definition: collection.cpp:112
Helper integration between Akonadi and Qt.
A representation of an agent instance.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Sun May 24 2020 22:46:13 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.