Akonadi

agentbase.cpp
1 /*
2  SPDX-FileCopyrightText: 2006 Till Adam <[email protected]>
3  SPDX-FileCopyrightText: 2007 Volker Krause <[email protected]>
4  SPDX-FileCopyrightText: 2007 Bruno Virlet <[email protected]>
5  SPDX-FileCopyrightText: 2008 Kevin Krammer <[email protected]>
6 
7  SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9 
10 #include "agentbase.h"
11 #include "agentbase_p.h"
12 
13 #include "agentconfigurationdialog.h"
14 #include "agentmanager.h"
15 #include "akonadifull-version.h"
16 #include "changerecorder.h"
17 #include "controladaptor.h"
18 #include "itemfetchjob.h"
19 #include "monitor_p.h"
20 #include "private/standarddirs_p.h"
21 #include "servermanager_p.h"
22 #include "session.h"
23 #include "session_p.h"
24 #include "statusadaptor.h"
25 
26 #include "akonadiagentbase_debug.h"
27 
28 #include <KLocalizedString>
29 
30 #include <KAboutData>
31 #include <kcoreaddons_version.h>
32 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
33 #include <Kdelibs4ConfigMigrator>
34 #endif
35 
36 #include <QCommandLineParser>
37 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
38 #include <QNetworkConfiguration>
39 #include <QNetworkConfigurationManager>
40 #else
41 #include <QNetworkInformation>
42 #endif
43 #include <QPointer>
44 #include <QSettings>
45 #include <QTimer>
46 
47 #include <QStandardPaths>
48 #include <signal.h>
49 #include <stdlib.h>
50 #if defined __GLIBC__
51 #include <malloc.h> // for dumping memory information
52 #endif
53 
54 #ifdef Q_OS_WIN
55 #include <Windows.h>
56 #endif
57 
58 #include <chrono>
59 #include <thread>
60 
61 using namespace Akonadi;
62 using namespace std::chrono_literals;
63 static AgentBase *sAgentBase = nullptr;
64 
66 {
67 }
68 
70 {
71 }
72 
73 void AgentBase::Observer::itemAdded(const Item &item, const Collection &collection)
74 {
75  Q_UNUSED(item)
76  Q_UNUSED(collection)
77  if (sAgentBase) {
78  sAgentBase->d_ptr->changeProcessed();
79  }
80 }
81 
82 void AgentBase::Observer::itemChanged(const Item &item, const QSet<QByteArray> &partIdentifiers)
83 {
84  Q_UNUSED(item)
85  Q_UNUSED(partIdentifiers)
86  if (sAgentBase) {
87  sAgentBase->d_ptr->changeProcessed();
88  }
89 }
90 
92 {
93  Q_UNUSED(item)
94  if (sAgentBase) {
95  sAgentBase->d_ptr->changeProcessed();
96  }
97 }
98 
100 {
101  Q_UNUSED(collection)
102  Q_UNUSED(parent)
103  if (sAgentBase) {
104  sAgentBase->d_ptr->changeProcessed();
105  }
106 }
107 
109 {
110  Q_UNUSED(collection)
111  if (sAgentBase) {
112  sAgentBase->d_ptr->changeProcessed();
113  }
114 }
115 
117 {
118  Q_UNUSED(collection)
119  if (sAgentBase) {
120  sAgentBase->d_ptr->changeProcessed();
121  }
122 }
123 
125 {
126  Q_UNUSED(item)
127  Q_UNUSED(source)
128  Q_UNUSED(dest)
129  if (sAgentBase) {
130  sAgentBase->d_ptr->changeProcessed();
131  }
132 }
133 
135 {
136  Q_UNUSED(item)
137  Q_UNUSED(collection)
138  if (sAgentBase) {
139  // not implementation, let's disconnect the signal to enable optimizations in Monitor
140  QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemLinked, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemLinked);
141  sAgentBase->d_ptr->changeProcessed();
142  }
143 }
144 
146 {
147  Q_UNUSED(item)
148  Q_UNUSED(collection)
149  if (sAgentBase) {
150  // not implementation, let's disconnect the signal to enable optimizations in Monitor
151  QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemUnlinked, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemUnlinked);
152  sAgentBase->d_ptr->changeProcessed();
153  }
154 }
155 
157 {
158  Q_UNUSED(collection)
159  Q_UNUSED(source)
160  Q_UNUSED(dest)
161  if (sAgentBase) {
162  sAgentBase->d_ptr->changeProcessed();
163  }
164 }
165 
166 void AgentBase::ObserverV2::collectionChanged(const Akonadi::Collection &collection, const QSet<QByteArray> &changedAttributes)
167 {
168  Q_UNUSED(changedAttributes)
169  collectionChanged(collection);
170 }
171 
172 void AgentBase::ObserverV3::itemsFlagsChanged(const Akonadi::Item::List &items, const QSet<QByteArray> &addedFlags, const QSet<QByteArray> &removedFlags)
173 {
174  Q_UNUSED(items)
175  Q_UNUSED(addedFlags)
176  Q_UNUSED(removedFlags)
177 
178  if (sAgentBase) {
179  // not implementation, let's disconnect the signal to enable optimizations in Monitor
180  QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsFlagsChanged, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsFlagsChanged);
181  sAgentBase->d_ptr->changeProcessed();
182  }
183 }
184 
185 void AgentBase::ObserverV3::itemsMoved(const Akonadi::Item::List &items, const Collection &sourceCollection, const Collection &destinationCollection)
186 {
187  Q_UNUSED(items)
188  Q_UNUSED(sourceCollection)
189  Q_UNUSED(destinationCollection)
190 
191  if (sAgentBase) {
192  // not implementation, let's disconnect the signal to enable optimizations in Monitor
193  QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsMoved, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsMoved);
194  sAgentBase->d_ptr->changeProcessed();
195  }
196 }
197 
199 {
200  Q_UNUSED(items)
201 
202  if (sAgentBase) {
203  // not implementation, let's disconnect the signal to enable optimizations in Monitor
204  QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsRemoved, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsRemoved);
205  sAgentBase->d_ptr->changeProcessed();
206  }
207 }
208 
210 {
211  Q_UNUSED(items)
212  Q_UNUSED(collection)
213 
214  if (sAgentBase) {
215  // not implementation, let's disconnect the signal to enable optimizations in Monitor
216  QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsLinked, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsLinked);
217  sAgentBase->d_ptr->changeProcessed();
218  }
219 }
220 
222 {
223  Q_UNUSED(items)
224  Q_UNUSED(collection)
225 
226  if (sAgentBase) {
227  // not implementation, let's disconnect the signal to enable optimizations in Monitor
228  QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsUnlinked, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsUnlinked);
229  sAgentBase->d_ptr->changeProcessed();
230  }
231 }
232 
234 {
235  Q_UNUSED(tag)
236 
237  if (sAgentBase) {
238  // not implementation, let's disconnect the signal to enable optimization in Monitor
239  QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::tagAdded, sAgentBase->d_ptr.get(), &AgentBasePrivate::tagAdded);
240  sAgentBase->d_ptr->changeProcessed();
241  }
242 }
243 
245 {
246  Q_UNUSED(tag)
247 
248  if (sAgentBase) {
249  // not implementation, let's disconnect the signal to enable optimization in Monitor
250  QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::tagChanged, sAgentBase->d_ptr.get(), &AgentBasePrivate::tagChanged);
251  sAgentBase->d_ptr->changeProcessed();
252  }
253 }
254 
256 {
257  Q_UNUSED(tag)
258 
259  if (sAgentBase) {
260  // not implementation, let's disconnect the signal to enable optimization in Monitor
261  QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::tagRemoved, sAgentBase->d_ptr.get(), &AgentBasePrivate::tagRemoved);
262  sAgentBase->d_ptr->changeProcessed();
263  }
264 }
265 
266 void AgentBase::ObserverV4::itemsTagsChanged(const Item::List &items, const QSet<Tag> &addedTags, const QSet<Tag> &removedTags)
267 {
268  Q_UNUSED(items)
269  Q_UNUSED(addedTags)
270  Q_UNUSED(removedTags)
271 
272  if (sAgentBase) {
273  // not implementation, let's disconnect the signal to enable optimization in Monitor
274  QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsTagsChanged, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsTagsChanged);
275  sAgentBase->d_ptr->changeProcessed();
276  }
277 }
278 
280 {
281  Q_UNUSED(relation)
282 
283  if (sAgentBase) {
284  // not implementation, let's disconnect the signal to enable optimization in Monitor
285  QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::relationAdded, sAgentBase->d_ptr.get(), &AgentBasePrivate::relationAdded);
286  sAgentBase->d_ptr->changeProcessed();
287  }
288 }
289 
291 {
292  Q_UNUSED(relation)
293 
294  if (sAgentBase) {
295  // not implementation, let's disconnect the signal to enable optimization in Monitor
296  QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::relationRemoved, sAgentBase->d_ptr.get(), &AgentBasePrivate::relationRemoved);
297  sAgentBase->d_ptr->changeProcessed();
298  }
299 }
300 
302  const Akonadi::Relation::List &addedRelations,
303  const Akonadi::Relation::List &removedRelations)
304 {
305  Q_UNUSED(items)
306  Q_UNUSED(addedRelations)
307  Q_UNUSED(removedRelations)
308 
309  if (sAgentBase) {
310  // not implementation, let's disconnect the signal to enable optimization in Monitor
311  disconnect(sAgentBase->changeRecorder(), &Monitor::itemsRelationsChanged, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsRelationsChanged);
312  sAgentBase->d_ptr->changeProcessed();
313  }
314 }
315 
316 /// @cond PRIVATE
317 
318 AgentBasePrivate::AgentBasePrivate(AgentBase *parent)
319  : q_ptr(parent)
320  , mStatusCode(AgentBase::Idle)
321  , mProgress(0)
322  , mNeedsNetwork(false)
323  , mOnline(false)
324  , mDesiredOnlineState(false)
325  , mPendingQuit(false)
326  , mSettings(nullptr)
327  , mChangeRecorder(nullptr)
328  , mTracer(nullptr)
329  , mObserver(nullptr)
330  , mPowerInterface(nullptr)
331  , mTemporaryOfflineTimer(nullptr)
332  , mEventLoopLocker(nullptr)
333 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
334  , mNetworkManager(nullptr)
335 #endif
336 {
337  Internal::setClientType(Internal::Agent);
338 }
339 
340 AgentBasePrivate::~AgentBasePrivate()
341 {
342  mChangeRecorder->setConfig(nullptr);
343  delete mSettings;
344 }
345 
346 void AgentBasePrivate::init()
347 {
348  Q_Q(AgentBase);
349 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
350  Kdelibs4ConfigMigrator migrate(mId);
351  migrate.setConfigFiles(QStringList() << QStringLiteral("%1rc").arg(mId));
352  migrate.migrate();
353 #endif
354  /**
355  * Create a default session for this process.
356  */
357  SessionPrivate::createDefaultSession(mId.toLatin1());
358 
359  mTracer =
360  new org::freedesktop::Akonadi::Tracer(ServerManager::serviceName(ServerManager::Server), QStringLiteral("/tracing"), QDBusConnection::sessionBus(), q);
361 
362  new Akonadi__ControlAdaptor(q);
363  new Akonadi__StatusAdaptor(q);
364  if (!QDBusConnection::sessionBus().registerObject(QStringLiteral("/"), q, QDBusConnection::ExportAdaptors)) {
365  Q_EMIT q->error(i18n("Unable to register object at dbus: %1", QDBusConnection::sessionBus().lastError().message()));
366  }
367 
369 
370  mChangeRecorder = new ChangeRecorder(q);
371  mChangeRecorder->setObjectName(QStringLiteral("AgentBaseChangeRecorder"));
372  mChangeRecorder->ignoreSession(Session::defaultSession());
373  mChangeRecorder->itemFetchScope().setCacheOnly(true);
374  mChangeRecorder->setConfig(mSettings);
375 
376  mDesiredOnlineState = mSettings->value(QStringLiteral("Agent/DesiredOnlineState"), true).toBool();
377  mOnline = mDesiredOnlineState;
378 
379  // reinitialize the status message now that online state is available
380  mStatusMessage = defaultReadyMessage();
381 
382  mName = mSettings->value(QStringLiteral("Agent/Name")).toString();
383  if (mName.isEmpty()) {
384  mName = mSettings->value(QStringLiteral("Resource/Name")).toString();
385  if (!mName.isEmpty()) {
386  mSettings->remove(QStringLiteral("Resource/Name"));
387  mSettings->setValue(QStringLiteral("Agent/Name"), mName);
388  }
389  }
390 
391  connect(mChangeRecorder, &Monitor::itemAdded, this, &AgentBasePrivate::itemAdded);
392  connect(mChangeRecorder, &Monitor::itemChanged, this, &AgentBasePrivate::itemChanged);
393  connect(mChangeRecorder, &Monitor::collectionAdded, this, &AgentBasePrivate::collectionAdded);
394  connect(mChangeRecorder,
395  qOverload<const Collection &>(&ChangeRecorder::collectionChanged),
396  this,
397  qOverload<const Collection &>(&AgentBasePrivate::collectionChanged));
398  connect(mChangeRecorder,
399  qOverload<const Collection &, const QSet<QByteArray> &>(&ChangeRecorder::collectionChanged),
400  this,
401  qOverload<const Collection &, const QSet<QByteArray> &>(&AgentBasePrivate::collectionChanged));
402  connect(mChangeRecorder, &Monitor::collectionMoved, this, &AgentBasePrivate::collectionMoved);
403  connect(mChangeRecorder, &Monitor::collectionRemoved, this, &AgentBasePrivate::collectionRemoved);
404  connect(mChangeRecorder, &Monitor::collectionSubscribed, this, &AgentBasePrivate::collectionSubscribed);
405  connect(mChangeRecorder, &Monitor::collectionUnsubscribed, this, &AgentBasePrivate::collectionUnsubscribed);
406 
407  connect(q, qOverload<int, const QString &>(&AgentBase::status), this, &AgentBasePrivate::slotStatus);
408  connect(q, &AgentBase::percent, this, &AgentBasePrivate::slotPercent);
409  connect(q, &AgentBase::warning, this, &AgentBasePrivate::slotWarning);
410  connect(q, &AgentBase::error, this, &AgentBasePrivate::slotError);
411 
412  mPowerInterface = new QDBusInterface(QStringLiteral("org.kde.Solid.PowerManagement"),
413  QStringLiteral("/org/kde/Solid/PowerManagement/Actions/SuspendSession"),
414  QStringLiteral("org.kde.Solid.PowerManagement.Actions.SuspendSession"),
416  this);
417  if (mPowerInterface->isValid()) {
418  connect(mPowerInterface, SIGNAL(resumingFromSuspend()), this, SLOT(slotResumedFromSuspend())); // clazy:exclude=old-style-connect
419  } else {
420  delete mPowerInterface;
421  mPowerInterface = nullptr;
422  }
423 
424  // Use reference counting to allow agents to finish internal jobs when the
425  // agent is stopped.
426  mEventLoopLocker = new QEventLoopLocker();
427 
428  mResourceTypeName = AgentManager::self()->instance(mId).type().name();
429  setProgramName();
430 
431  QTimer::singleShot(0s, q, [this] {
432  delayedInit();
433  });
434 }
435 
436 void AgentBasePrivate::delayedInit()
437 {
438  Q_Q(AgentBase);
439 
440  const QString serviceId = ServerManager::agentServiceName(ServerManager::Agent, mId);
441  if (!QDBusConnection::sessionBus().registerService(serviceId)) {
442  qCCritical(AKONADIAGENTBASE_LOG) << "Unable to register service" << serviceId << "at dbus:" << QDBusConnection::sessionBus().lastError().message();
443  }
444  q->setOnlineInternal(mDesiredOnlineState);
445 
447 }
448 
449 void AgentBasePrivate::setProgramName()
450 {
451  // ugly, really ugly, if you find another solution, change it and blame me for this code (Andras)
452  QString programName = mResourceTypeName;
453  if (!mName.isEmpty()) {
454  programName = i18nc("Name and type of Akonadi resource", "%1 of type %2", mName, mResourceTypeName);
455  }
456 
458 }
459 
460 void AgentBasePrivate::itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection)
461 {
462  if (mObserver) {
463  mObserver->itemAdded(item, collection);
464  } else {
465  changeProcessed();
466  }
467 }
468 
469 void AgentBasePrivate::itemChanged(const Akonadi::Item &item, const QSet<QByteArray> &partIdentifiers)
470 {
471  if (mObserver) {
472  mObserver->itemChanged(item, partIdentifiers);
473  } else {
474  changeProcessed();
475  }
476 }
477 
478 void AgentBasePrivate::itemMoved(const Akonadi::Item &item, const Akonadi::Collection &source, const Akonadi::Collection &dest)
479 {
480  auto observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
481  if (mObserver) {
482  // inter-resource moves, requires we know which resources the source and destination are in though
483  if (!source.resource().isEmpty() && !dest.resource().isEmpty()) {
484  if (source.resource() != dest.resource()) {
485  if (source.resource() == q_ptr->identifier()) { // moved away from us
486  Akonadi::Item i(item);
487  i.setParentCollection(source);
488  mObserver->itemRemoved(i);
489  } else if (dest.resource() == q_ptr->identifier()) { // moved to us
490  mObserver->itemAdded(item, dest);
491  } else if (observer2) {
492  observer2->itemMoved(item, source, dest);
493  } else {
494  // not for us, not sure if we should get here at all
495  changeProcessed();
496  }
497  return;
498  }
499  }
500  // intra-resource move
501  if (observer2) {
502  observer2->itemMoved(item, source, dest);
503  } else {
504  // ### we cannot just call itemRemoved here as this will already trigger changeProcessed()
505  // so, just itemAdded() is good enough as no resource can have implemented intra-resource moves anyway
506  // without using ObserverV2
507  mObserver->itemAdded(item, dest);
508  // mObserver->itemRemoved( item );
509  }
510  }
511 }
512 
513 void AgentBasePrivate::itemRemoved(const Akonadi::Item &item)
514 {
515  if (mObserver) {
516  mObserver->itemRemoved(item);
517  } else {
518  changeProcessed();
519  }
520 }
521 
522 void AgentBasePrivate::itemLinked(const Akonadi::Item &item, const Akonadi::Collection &collection)
523 {
524  auto observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
525  if (observer2) {
526  observer2->itemLinked(item, collection);
527  } else {
528  changeProcessed();
529  }
530 }
531 
532 void AgentBasePrivate::itemUnlinked(const Akonadi::Item &item, const Akonadi::Collection &collection)
533 {
534  auto observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
535  if (observer2) {
536  observer2->itemUnlinked(item, collection);
537  } else {
538  changeProcessed();
539  }
540 }
541 
542 void AgentBasePrivate::itemsFlagsChanged(const Akonadi::Item::List &items, const QSet<QByteArray> &addedFlags, const QSet<QByteArray> &removedFlags)
543 {
544  auto observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver);
545  if (observer3) {
546  observer3->itemsFlagsChanged(items, addedFlags, removedFlags);
547  } else {
548  Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available");
549  }
550 }
551 
552 void AgentBasePrivate::itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &source, const Akonadi::Collection &destination)
553 {
554  auto observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver);
555  if (observer3) {
556  observer3->itemsMoved(items, source, destination);
557  } else {
558  Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available");
559  }
560 }
561 
562 void AgentBasePrivate::itemsRemoved(const Akonadi::Item::List &items)
563 {
564  auto observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver);
565  if (observer3) {
566  observer3->itemsRemoved(items);
567  } else {
568  Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available");
569  }
570 }
571 
572 void AgentBasePrivate::itemsLinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection)
573 {
574  if (!mObserver) {
575  changeProcessed();
576  return;
577  }
578 
579  auto observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver);
580  if (observer3) {
581  observer3->itemsLinked(items, collection);
582  } else {
583  Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available");
584  }
585 }
586 
587 void AgentBasePrivate::itemsUnlinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection)
588 {
589  if (!mObserver) {
590  changeProcessed();
591  return;
592  }
593 
594  auto observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver);
595  if (observer3) {
596  observer3->itemsUnlinked(items, collection);
597  } else {
598  Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available");
599  }
600 }
601 
602 void AgentBasePrivate::tagAdded(const Akonadi::Tag &tag)
603 {
604  auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
605  if (observer4) {
606  observer4->tagAdded(tag);
607  } else {
608  changeProcessed();
609  }
610 }
611 
612 void AgentBasePrivate::tagChanged(const Akonadi::Tag &tag)
613 {
614  auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
615  if (observer4) {
616  observer4->tagChanged(tag);
617  } else {
618  changeProcessed();
619  }
620 }
621 
622 void AgentBasePrivate::tagRemoved(const Akonadi::Tag &tag)
623 {
624  auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
625  if (observer4) {
626  observer4->tagRemoved(tag);
627  } else {
628  changeProcessed();
629  }
630 }
631 
632 void AgentBasePrivate::itemsTagsChanged(const Akonadi::Item::List &items, const QSet<Akonadi::Tag> &addedTags, const QSet<Akonadi::Tag> &removedTags)
633 {
634  auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
635  if (observer4) {
636  observer4->itemsTagsChanged(items, addedTags, removedTags);
637  } else {
638  changeProcessed();
639  }
640 }
641 
642 void AgentBasePrivate::relationAdded(const Akonadi::Relation &relation)
643 {
644  auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
645  if (observer4) {
646  observer4->relationAdded(relation);
647  } else {
648  changeProcessed();
649  }
650 }
651 
652 void AgentBasePrivate::relationRemoved(const Akonadi::Relation &relation)
653 {
654  auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
655  if (observer4) {
656  observer4->relationRemoved(relation);
657  } else {
658  changeProcessed();
659  }
660 }
661 
662 void AgentBasePrivate::itemsRelationsChanged(const Akonadi::Item::List &items,
663  const Akonadi::Relation::List &addedRelations,
664  const Akonadi::Relation::List &removedRelations)
665 {
666  auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
667  if (observer4) {
668  observer4->itemsRelationsChanged(items, addedRelations, removedRelations);
669  } else {
670  changeProcessed();
671  }
672 }
673 
674 void AgentBasePrivate::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent)
675 {
676  if (mObserver) {
677  mObserver->collectionAdded(collection, parent);
678  } else {
679  changeProcessed();
680  }
681 }
682 
683 void AgentBasePrivate::collectionChanged(const Akonadi::Collection &collection)
684 {
685  auto observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
686  if (mObserver && observer2 == nullptr) { // For ObserverV2 we use the variant with the part identifiers
687  mObserver->collectionChanged(collection);
688  } else if (!mObserver) {
689  changeProcessed();
690  }
691 }
692 
693 void AgentBasePrivate::collectionChanged(const Akonadi::Collection &collection, const QSet<QByteArray> &changedAttributes)
694 {
695  auto observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
696  if (observer2) {
697  observer2->collectionChanged(collection, changedAttributes);
698  } else {
699  changeProcessed();
700  }
701 }
702 
703 void AgentBasePrivate::collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &source, const Akonadi::Collection &dest)
704 {
705  auto observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
706  if (observer2) {
707  observer2->collectionMoved(collection, source, dest);
708  } else if (mObserver) {
709  // ### we cannot just call collectionRemoved here as this will already trigger changeProcessed()
710  // so, just collectionAdded() is good enough as no resource can have implemented intra-resource moves anyway
711  // without using ObserverV2
712  mObserver->collectionAdded(collection, dest);
713  } else {
714  changeProcessed();
715  }
716 }
717 
718 void AgentBasePrivate::collectionRemoved(const Akonadi::Collection &collection)
719 {
720  if (mObserver) {
721  mObserver->collectionRemoved(collection);
722  } else {
723  changeProcessed();
724  }
725 }
726 
727 void AgentBasePrivate::collectionSubscribed(const Akonadi::Collection &collection, const Akonadi::Collection &parent)
728 {
729  Q_UNUSED(collection)
730  Q_UNUSED(parent)
731  changeProcessed();
732 }
733 
734 void AgentBasePrivate::collectionUnsubscribed(const Akonadi::Collection &collection)
735 {
736  Q_UNUSED(collection)
737  changeProcessed();
738 }
739 
740 void AgentBasePrivate::changeProcessed()
741 {
742  mChangeRecorder->changeProcessed();
743  QTimer::singleShot(0s, mChangeRecorder, &ChangeRecorder::replayNext);
744 }
745 
746 void AgentBasePrivate::slotStatus(int status, const QString &message)
747 {
748  mStatusMessage = message;
749  mStatusCode = 0;
750 
751  switch (status) {
752  case AgentBase::Idle:
753  if (mStatusMessage.isEmpty()) {
754  mStatusMessage = defaultReadyMessage();
755  }
756 
757  mStatusCode = 0;
758  break;
759  case AgentBase::Running:
760  if (mStatusMessage.isEmpty()) {
761  mStatusMessage = defaultSyncingMessage();
762  }
763 
764  mStatusCode = 1;
765  break;
766  case AgentBase::Broken:
767  if (mStatusMessage.isEmpty()) {
768  mStatusMessage = defaultErrorMessage();
769  }
770 
771  mStatusCode = 2;
772  break;
773 
775  if (mStatusMessage.isEmpty()) {
776  mStatusMessage = defaultUnconfiguredMessage();
777  }
778 
779  mStatusCode = 3;
780  break;
781 
782  default:
783  Q_ASSERT(!"Unknown status passed");
784  break;
785  }
786 }
787 
788 void AgentBasePrivate::slotPercent(int progress)
789 {
790  mProgress = progress;
791 }
792 
793 void AgentBasePrivate::slotWarning(const QString &message)
794 {
795  mTracer->warning(QStringLiteral("AgentBase(%1)").arg(mId), message);
796 }
797 
798 void AgentBasePrivate::slotError(const QString &message)
799 {
800  mTracer->error(QStringLiteral("AgentBase(%1)").arg(mId), message);
801 }
802 
803 void AgentBasePrivate::slotNetworkStatusChange(bool isOnline)
804 {
805  Q_UNUSED(isOnline)
806  Q_Q(AgentBase);
807  q->setOnlineInternal(mDesiredOnlineState);
808 }
809 
810 void AgentBasePrivate::slotResumedFromSuspend()
811 {
812  if (mNeedsNetwork) {
813 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
814  slotNetworkStatusChange(mNetworkManager->isOnline());
815 #else
816  slotNetworkStatusChange(QNetworkInformation::instance()->reachability() != QNetworkInformation::Reachability::Online);
817 #endif
818  }
819 }
820 
821 void AgentBasePrivate::slotTemporaryOfflineTimeout()
822 {
823  Q_Q(AgentBase);
824  q->setOnlineInternal(true);
825 }
826 
827 QString AgentBasePrivate::dumpNotificationListToString() const
828 {
829  return mChangeRecorder->dumpNotificationListToString();
830 }
831 
832 void AgentBasePrivate::dumpMemoryInfo() const
833 {
834  // Send it to stdout, so we can debug user problems.
835  // since you have to explicitly call this
836  // it won't flood users with release builds.
837  QTextStream stream(stdout);
838  stream << dumpMemoryInfoToString();
839 }
840 
841 QString AgentBasePrivate::dumpMemoryInfoToString() const
842 {
843  // man mallinfo for more info
844  QString str;
845 #if defined __GLIBC__
846  struct mallinfo mi;
847  mi = mallinfo();
848  QTextStream stream(&str);
849  stream << "Total non-mmapped bytes (arena): " << mi.arena << '\n'
850  << "# of free chunks (ordblks): " << mi.ordblks << '\n'
851  << "# of free fastbin blocks (smblks>: " << mi.smblks << '\n'
852  << "# of mapped regions (hblks): " << mi.hblks << '\n'
853  << "Bytes in mapped regions (hblkhd): " << mi.hblkhd << '\n'
854  << "Max. total allocated space (usmblks): " << mi.usmblks << '\n'
855  << "Free bytes held in fastbins (fsmblks):" << mi.fsmblks << '\n'
856  << "Total allocated space (uordblks): " << mi.uordblks << '\n'
857  << "Total free space (fordblks): " << mi.fordblks << '\n'
858  << "Topmost releasable block (keepcost): " << mi.keepcost << '\n';
859 #else
860  str = QLatin1String("mallinfo() not supported");
861 #endif
862  return str;
863 }
864 
865 AgentBase::AgentBase(const QString &id)
866  : d_ptr(new AgentBasePrivate(this))
867 {
868  sAgentBase = this;
869  d_ptr->mId = id;
870  d_ptr->init();
871 }
872 
873 AgentBase::AgentBase(AgentBasePrivate *d, const QString &id)
874  : d_ptr(d)
875 {
876  sAgentBase = this;
877  d_ptr->mId = id;
878  d_ptr->init();
879 }
880 
881 AgentBase::~AgentBase() = default;
882 
883 void AgentBase::debugAgent(int argc, char **argv)
884 {
885  Q_UNUSED(argc)
886 #ifdef Q_OS_WIN
887  if (qEnvironmentVariableIsSet("AKONADI_DEBUG_WAIT")) {
888  if (QByteArray(argv[0]).endsWith(QByteArray(qgetenv("AKONADI_DEBUG_WAIT") + QByteArrayLiteral(".exe")))) {
889  while (!IsDebuggerPresent()) {
890  std::this_thread::sleep_for(std::chrono::milliseconds(100));
891  }
892  DebugBreak();
893  }
894  }
895 #else
896  Q_UNUSED(argv)
897 #endif
898 }
899 
900 QString AgentBase::parseArguments(int argc, char **argv)
901 {
902  Q_UNUSED(argc)
903 
904  QCommandLineOption identifierOption(QStringLiteral("identifier"), i18n("Agent identifier"), QStringLiteral("argument"));
905  QCommandLineParser parser;
906  parser.addOption(identifierOption);
907  parser.addHelpOption();
908  parser.addVersionOption();
909  parser.process(*qApp);
910  parser.setApplicationDescription(i18n("Akonadi Agent"));
911 
912  if (!parser.isSet(identifierOption)) {
913  qCDebug(AKONADIAGENTBASE_LOG) << "Identifier argument missing";
914  exit(1);
915  }
916 
917  const QString identifier = parser.value(identifierOption);
918  if (identifier.isEmpty()) {
919  qCDebug(AKONADIAGENTBASE_LOG) << "Identifier argument is empty";
920  exit(1);
921  }
922 
924  QCoreApplication::setApplicationVersion(QStringLiteral(AKONADI_FULL_VERSION));
925 
926  const QFileInfo fi(QString::fromLocal8Bit(argv[0]));
927  // strip off full path and possible .exe suffix
928  const QString catalog = fi.baseName();
929 
930  auto translator = new QTranslator(qApp);
931  translator->load(catalog);
933 
934  return identifier;
935 }
936 
937 /// @endcond
938 
940 {
942  KAboutData::setApplicationData(r.aboutData());
943  return qApp->exec();
944 }
945 
946 int AgentBase::status() const
947 {
948  Q_D(const AgentBase);
949 
950  return d->mStatusCode;
951 }
952 
954 {
955  Q_D(const AgentBase);
956 
957  return d->mStatusMessage;
958 }
959 
961 {
962  Q_D(const AgentBase);
963 
964  return d->mProgress;
965 }
966 
968 {
969  Q_D(const AgentBase);
970 
971  return d->mProgressMessage;
972 }
973 
975 {
976  Q_D(const AgentBase);
977 
978  return d->mOnline;
979 }
980 
981 void AgentBase::setNeedsNetwork(bool needsNetwork)
982 {
983  Q_D(AgentBase);
984  if (d->mNeedsNetwork == needsNetwork) {
985  return;
986  }
987 
988  d->mNeedsNetwork = needsNetwork;
989 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
990  if (d->mNeedsNetwork) {
991  QT_WARNING_PUSH
992  QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
993  QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
994  d->mNetworkManager = new QNetworkConfigurationManager(this);
995  connect(d->mNetworkManager, &QNetworkConfigurationManager::onlineStateChanged, d, &AgentBasePrivate::slotNetworkStatusChange, Qt::UniqueConnection);
996  QT_WARNING_POP
997  } else {
998  delete d->mNetworkManager;
999  d->mNetworkManager = nullptr;
1000  setOnlineInternal(d->mDesiredOnlineState);
1001  }
1002 #else
1003  QNetworkInformation::load(QNetworkInformation::Feature::Reachability);
1004  connect(QNetworkInformation::instance(), &QNetworkInformation::reachabilityChanged, this, [this, d](auto reachability) {
1005  d->slotNetworkStatusChange(reachability == QNetworkInformation::Reachability::Online);
1006  });
1007 #endif
1008 }
1009 
1010 void AgentBase::setOnline(bool state)
1011 {
1012  Q_D(AgentBase);
1013 
1014  if (d->mPendingQuit) {
1015  return;
1016  }
1017 
1018  d->mDesiredOnlineState = state;
1019  d->mSettings->setValue(QStringLiteral("Agent/DesiredOnlineState"), state);
1020  setOnlineInternal(state);
1021 }
1022 
1023 void AgentBase::setTemporaryOffline(int makeOnlineInSeconds)
1024 {
1025  Q_D(AgentBase);
1026 
1027  // if not currently online, avoid bringing it online after the timeout
1028  if (!d->mOnline) {
1029  return;
1030  }
1031 
1032  setOnlineInternal(false);
1033 
1034  if (!d->mTemporaryOfflineTimer) {
1035  d->mTemporaryOfflineTimer = new QTimer(d);
1036  d->mTemporaryOfflineTimer->setSingleShot(true);
1037  connect(d->mTemporaryOfflineTimer, &QTimer::timeout, d, &AgentBasePrivate::slotTemporaryOfflineTimeout);
1038  }
1039  d->mTemporaryOfflineTimer->setInterval(std::chrono::seconds{makeOnlineInSeconds});
1040  d->mTemporaryOfflineTimer->start();
1041 }
1042 
1043 void AgentBase::setOnlineInternal(bool state)
1044 {
1045  Q_D(AgentBase);
1046  if (state && d->mNeedsNetwork) {
1047 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1048  if (!d->mNetworkManager->isOnline()) {
1049  // Don't go online if the resource needs network but there is none
1050  state = false;
1051  }
1052 #else
1053  if (QNetworkInformation::instance()->reachability() != QNetworkInformation::Reachability::Online) {
1054  // Don't go online if the resource needs network but there is none
1055  state = false;
1056  }
1057 #endif
1058  }
1059  d->mOnline = state;
1060 
1061  if (d->mTemporaryOfflineTimer) {
1062  d->mTemporaryOfflineTimer->stop();
1063  }
1064 
1065  const QString newMessage = d->defaultReadyMessage();
1066  if (d->mStatusMessage != newMessage && d->mStatusCode != AgentBase::Broken) {
1067  Q_EMIT status(d->mStatusCode, newMessage);
1068  }
1069 
1070  doSetOnline(state);
1071  Q_EMIT onlineChanged(state);
1072 }
1073 
1074 void AgentBase::doSetOnline(bool online)
1075 {
1076  Q_UNUSED(online)
1077 }
1078 
1079 KAboutData AgentBase::aboutData() const
1080 {
1081  // akonadi_google_resource_1 -> org.kde.akonadi_google_resource
1082  const QString desktopEntry = QLatin1String("org.kde.") + qApp->applicationName().remove(QRegularExpression(QStringLiteral("_[0-9]+$")));
1083 
1084  KAboutData data(qApp->applicationName(), agentName(), qApp->applicationVersion());
1085  data.setDesktopFileName(desktopEntry);
1086  return data;
1087 }
1088 
1089 void AgentBase::configure(WId windowId)
1090 {
1091  Q_UNUSED(windowId)
1092 
1093  // Fallback if the agent implements the new plugin-based configuration,
1094  // but someone calls the deprecated configure() method
1095  auto instance = Akonadi::AgentManager::self()->instance(identifier());
1096  QPointer<AgentConfigurationDialog> dialog = new AgentConfigurationDialog(instance, nullptr);
1097  if (dialog->exec()) {
1099  } else {
1101  }
1102  delete dialog;
1103 }
1104 
1105 #ifdef Q_OS_WIN // krazy:exclude=cpp
1106 void AgentBase::configure(qlonglong windowId)
1107 {
1108  configure(static_cast<WId>(windowId));
1109 }
1110 #endif
1111 
1113 {
1114  const bool registered = QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.akonaditray"));
1115  if (!registered) {
1116  return 0;
1117  }
1118 
1119  QDBusInterface dbus(QStringLiteral("org.freedesktop.akonaditray"), QStringLiteral("/Actions"), QStringLiteral("org.freedesktop.Akonadi.Tray"));
1120  const QDBusMessage reply = dbus.call(QStringLiteral("getWinId"));
1121 
1122  if (reply.type() == QDBusMessage::ErrorMessage) {
1123  return 0;
1124  }
1125 
1126  const WId winid = static_cast<WId>(reply.arguments().at(0).toLongLong());
1127 
1128  return winid;
1129 }
1130 
1131 void AgentBase::quit()
1132 {
1133  Q_D(AgentBase);
1134  aboutToQuit();
1135 
1136  if (d->mSettings) {
1137  d->mChangeRecorder->setConfig(nullptr);
1138  d->mSettings->sync();
1139  delete d->mSettings;
1140  d->mSettings = nullptr;
1141  }
1142 
1143  delete d->mEventLoopLocker;
1144  d->mEventLoopLocker = nullptr;
1145 }
1146 
1148 {
1149  Q_D(AgentBase);
1150  d->mPendingQuit = true;
1151 }
1152 
1154 {
1155  Q_D(AgentBase);
1156  // prevent the monitor from picking up deletion signals for our own data if we are a resource
1157  // and thus avoid that we kill our own data as last act before our own death
1158  d->mChangeRecorder->blockSignals(true);
1159 
1160  aboutToQuit();
1161 
1162  const QString fileName = d->mSettings->fileName();
1163 
1164  /*
1165  * First destroy the settings object...
1166  */
1167  d->mChangeRecorder->setConfig(nullptr);
1168  delete d->mSettings;
1169  d->mSettings = nullptr;
1170 
1171  /*
1172  * ... then remove the file from hd.
1173  */
1174  if (!QFile::remove(fileName)) {
1175  qCWarning(AKONADIAGENTBASE_LOG) << "Impossible to remove " << fileName;
1176  }
1177 
1178  /*
1179  * ... and remove the changes file from hd.
1180  */
1181  const QString changeDataFileName = fileName + QStringLiteral("_changes.dat");
1182  if (!QFile::remove(changeDataFileName)) {
1183  qCWarning(AKONADIAGENTBASE_LOG) << "Impossible to remove " << changeDataFileName;
1184  }
1185 
1186  /*
1187  * ... and also remove the agent configuration file if there is one.
1188  */
1190  if (!QFile::remove(configFile)) {
1191  qCWarning(AKONADIAGENTBASE_LOG) << "Impossible to remove config file " << configFile;
1192  }
1193 
1194  delete d->mEventLoopLocker;
1195  d->mEventLoopLocker = nullptr;
1196 }
1197 
1199 {
1200  Q_D(AgentBase);
1201 
1202  // TODO in theory we should re-connect change recorder signals here that we disconnected previously
1203  d->mObserver = observer;
1204 
1205  const bool hasObserverV3 = (dynamic_cast<AgentBase::ObserverV3 *>(d->mObserver) != nullptr);
1206  const bool hasObserverV4 = (dynamic_cast<AgentBase::ObserverV4 *>(d->mObserver) != nullptr);
1207 
1208  disconnect(d->mChangeRecorder, &Monitor::tagAdded, d, &AgentBasePrivate::tagAdded);
1209  disconnect(d->mChangeRecorder, &Monitor::tagChanged, d, &AgentBasePrivate::tagChanged);
1210  disconnect(d->mChangeRecorder, &Monitor::tagRemoved, d, &AgentBasePrivate::tagRemoved);
1211  disconnect(d->mChangeRecorder, &Monitor::itemsTagsChanged, d, &AgentBasePrivate::itemsTagsChanged);
1212  disconnect(d->mChangeRecorder, &Monitor::itemsFlagsChanged, d, &AgentBasePrivate::itemsFlagsChanged);
1213  disconnect(d->mChangeRecorder, &Monitor::itemsMoved, d, &AgentBasePrivate::itemsMoved);
1214  disconnect(d->mChangeRecorder, &Monitor::itemsRemoved, d, &AgentBasePrivate::itemsRemoved);
1215  disconnect(d->mChangeRecorder, &Monitor::itemsLinked, d, &AgentBasePrivate::itemsLinked);
1216  disconnect(d->mChangeRecorder, &Monitor::itemsUnlinked, d, &AgentBasePrivate::itemsUnlinked);
1217  disconnect(d->mChangeRecorder, &Monitor::itemMoved, d, &AgentBasePrivate::itemMoved);
1218  disconnect(d->mChangeRecorder, &Monitor::itemRemoved, d, &AgentBasePrivate::itemRemoved);
1219  disconnect(d->mChangeRecorder, &Monitor::itemLinked, d, &AgentBasePrivate::itemLinked);
1220  disconnect(d->mChangeRecorder, &Monitor::itemUnlinked, d, &AgentBasePrivate::itemUnlinked);
1221 
1222  if (hasObserverV4) {
1223  connect(d->mChangeRecorder, &Monitor::tagAdded, d, &AgentBasePrivate::tagAdded);
1224  connect(d->mChangeRecorder, &Monitor::tagChanged, d, &AgentBasePrivate::tagChanged);
1225  connect(d->mChangeRecorder, &Monitor::tagRemoved, d, &AgentBasePrivate::tagRemoved);
1226  connect(d->mChangeRecorder, &Monitor::itemsTagsChanged, d, &AgentBasePrivate::itemsTagsChanged);
1227  }
1228 
1229  if (hasObserverV3) {
1230  connect(d->mChangeRecorder, &Monitor::itemsFlagsChanged, d, &AgentBasePrivate::itemsFlagsChanged);
1231  connect(d->mChangeRecorder, &Monitor::itemsMoved, d, &AgentBasePrivate::itemsMoved);
1232  connect(d->mChangeRecorder, &Monitor::itemsRemoved, d, &AgentBasePrivate::itemsRemoved);
1233  connect(d->mChangeRecorder, &Monitor::itemsLinked, d, &AgentBasePrivate::itemsLinked);
1234  connect(d->mChangeRecorder, &Monitor::itemsUnlinked, d, &AgentBasePrivate::itemsUnlinked);
1235  } else {
1236  // V2 - don't connect these if we have V3
1237  connect(d->mChangeRecorder, &Monitor::itemMoved, d, &AgentBasePrivate::itemMoved);
1238  connect(d->mChangeRecorder, &Monitor::itemRemoved, d, &AgentBasePrivate::itemRemoved);
1239  connect(d->mChangeRecorder, &Monitor::itemLinked, d, &AgentBasePrivate::itemLinked);
1240  connect(d->mChangeRecorder, &Monitor::itemUnlinked, d, &AgentBasePrivate::itemUnlinked);
1241  }
1242 }
1243 
1245 {
1246  return d_ptr->mId;
1247 }
1248 
1250 {
1251  Q_D(AgentBase);
1252  if (name == d->mName) {
1253  return;
1254  }
1255 
1256  // TODO: rename collection
1257  d->mName = name;
1258 
1259  if (d->mName.isEmpty() || d->mName == d->mId) {
1260  d->mSettings->remove(QStringLiteral("Resource/Name"));
1261  d->mSettings->remove(QStringLiteral("Agent/Name"));
1262  } else {
1263  d->mSettings->setValue(QStringLiteral("Agent/Name"), d->mName);
1264  }
1265 
1266  d->mSettings->sync();
1267 
1268  d->setProgramName();
1269 
1270  Q_EMIT agentNameChanged(d->mName);
1271 }
1272 
1274 {
1275  Q_D(const AgentBase);
1276  if (d->mName.isEmpty()) {
1277  return d->mId;
1278  } else {
1279  return d->mName;
1280  }
1281 }
1282 
1284 {
1285  Q_D(AgentBase);
1286  d->changeProcessed();
1287 }
1288 
1290 {
1291  return d_ptr->mChangeRecorder;
1292 }
1293 
1294 KSharedConfigPtr AgentBase::config()
1295 {
1296  return KSharedConfig::openConfig();
1297 }
1298 
1299 void AgentBase::abort()
1300 {
1302 }
1303 
1304 void AgentBase::reconfigure()
1305 {
1307 }
1308 
1309 #include "moc_agentbase.cpp"
1310 #include "moc_agentbase_p.cpp"
void relationRemoved(const Akonadi::Relation &relation)
This signal is emitted if a monitored relation is removed from the server storage.
virtual void itemsRemoved(const Akonadi::Item::List &items)
Reimplement to handle batch notification about items deletion.
Definition: agentbase.cpp:198
void collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent)
This signal is emitted if a new collection has been added to a monitored collection in the Akonadi st...
virtual void configure(WId windowId)
This method is called whenever the agent shall show its configuration dialog to the user.
Definition: agentbase.cpp:1089
@ Idle
The agent does currently nothing.
Definition: agentbase.h:422
virtual void itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection)
Reimplement to handle adding of new items.
Definition: agentbase.cpp:73
void collectionRemoved(const Akonadi::Collection &collection)
This signal is emitted if a monitored collection has been removed from the Akonadi storage.
virtual void doSetOnline(bool online)
This method is called whenever the online status has changed.
Definition: agentbase.cpp:1074
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
void collectionSubscribed(const Akonadi::Collection &collection, const Akonadi::Collection &parent)
This signal is emitted if a collection has been subscribed to by the user.
void itemsFlagsChanged(const Akonadi::Item::List &items, const QSet< QByteArray > &addedFlags, const QSet< QByteArray > &removedFlags)
This signal is emitted if flags of monitored items have changed.
void collectionChanged(const Akonadi::Collection &collection)
This signal is emitted if a monitored collection has been changed (properties or content).
AgentType type() const
Returns the agent type of this instance.
bool remove()
Q_EMITQ_EMIT
void setApplicationDescription(const QString &description)
WId winIdForDialogs() const
This method returns the windows id, which should be used for dialogs.
Definition: agentbase.cpp:1112
QString message() const const
An Akonadi Tag.
Definition: tag.h:25
virtual void relationRemoved(const Akonadi::Relation &relation)
Reimplement to handle relations being removed.
Definition: agentbase.cpp:290
static void setApplicationData(const KAboutData &aboutData)
void tagRemoved(const Akonadi::Tag &tag)
This signal is emitted if a monitored tag is removed from the server storage.
void collectionUnsubscribed(const Akonadi::Collection &collection)
This signal is emitted if a user unsubscribes from a collection.
void itemsRelationsChanged(const Akonadi::Item::List &items, const Akonadi::Relation::List &addedRelations, const Akonadi::Relation::List &removedRelations)
This signal is emitted if relations of monitored items have changed.
virtual void tagChanged(const Akonadi::Tag &tag)
Reimplement to handle tags changes.
Definition: agentbase.cpp:244
void itemsRemoved(const Akonadi::Item::List &items)
This signal is emitted if monitored items have been removed from Akonadi storage of items have been r...
void itemRemoved(const Akonadi::Item &item)
This signal is emitted if.
@ NotConfigured
The agent is lacking required configuration.
Definition: agentbase.h:425
virtual void itemLinked(const Akonadi::Item &item, const Akonadi::Collection &collection)
Reimplement to handle item linking.
Definition: agentbase.cpp:134
void relationAdded(const Akonadi::Relation &relation)
This signal is emitted if a relation has been added to Akonadi storage.
bool isOnline() const
Returns whether the agent is currently online.
Definition: agentbase.cpp:974
static QString serviceName(ServiceType serviceType)
Returns the namespaced D-Bus service name for serviceType.
void tagAdded(const Akonadi::Tag &tag)
This signal is emitted if a tag has been added to Akonadi storage.
Represents a collection of PIM items.
Definition: collection.h:61
QDBusReply< bool > isServiceRegistered(const QString &serviceName) const const
void itemsTagsChanged(const Akonadi::Item::List &items, const QSet< Akonadi::Tag > &addedTags, const QSet< Akonadi::Tag > &removedTags)
This signal is emitted if tags of monitored items have changed.
void setApplicationVersion(const QString &version)
QString writableLocation(QStandardPaths::StandardLocation type)
bool registerObject(const QString &path, QObject *object, QDBusConnection::RegisterOptions options)
An Akonadi Relation.
Definition: relation.h:39
QDBusMessage call(const QString &method, Args &&... args)
~AgentBase() override
Destroys the agent base.
void replayNext()
Replay the next change notification and erase the previous one from the record.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
virtual QString progressMessage() const
This method returns an i18n'ed description of the current progress.
Definition: agentbase.cpp:967
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
QDBusError lastError() const const
void agentNameChanged(const QString &name)
This signal is emitted whenever the name of the agent has changed.
static QString agentServiceName(ServiceAgentType agentType, const QString &identifier)
Returns the namespaced D-Bus service name for an agent of type agentType with agent identifier identi...
Records and replays change notification.
The interface for reacting on monitored or replayed changes.
Definition: agentbase.h:178
void itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination)
This is signal is emitted when multiple monitored items have been moved between two collections.
void percent(int progress)
This signal should be emitted whenever the progress of an action in the agent (e.g.
void itemsLinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection)
This signal is emitted if a reference to multiple items is added to a virtual collection.
AgentInstance instance(const QString &identifier) const
Returns the agent instance with the given identifier or an invalid agent instance if the identifier d...
static int init(int argc, char **argv)
Use this method in the main function of your agent application to initialize your agent subclass.
Definition: agentbase.h:453
void itemLinked(const Akonadi::Item &item, const Akonadi::Collection &collection)
This signal is emitted if a reference to an item is added to a virtual collection.
void setApplicationDisplayName(const QString &name)
QList< QVariant > arguments() const const
QString i18n(const char *text, const TYPE &arg...)
KSharedConfigPtr config()
Returns the config object for this Agent.
Definition: agentbase.cpp:1294
QDBusConnection sessionBus()
void changeProcessed()
Marks the current change as processes and replays the next change if change recording is enabled (noo...
Definition: agentbase.cpp:1283
QString fromLocal8Bit(const char *str, int size)
void onlineChanged(bool online)
Emitted when the online state changed.
void registerObserver(Observer *observer)
Registers the given observer for reacting on monitored or recorded changes.
Definition: agentbase.cpp:1198
bool installTranslator(QTranslator *translationFile)
virtual void itemsLinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection)
Reimplement to handle batch notifications about items linking.
Definition: agentbase.cpp:209
virtual void itemsTagsChanged(const Akonadi::Item::List &items, const QSet< Akonadi::Tag > &addedTags, const QSet< Akonadi::Tag > &removedTags)
Reimplement to handle items tagging.
Definition: agentbase.cpp:266
void process(const QStringList &arguments)
virtual void itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &sourceCollection, const Akonadi::Collection &destinationCollection)
Reimplement to handle batch notification about items move.
Definition: agentbase.cpp:185
void setOnline(bool state)
Sets whether the agent shall be online or not.
Definition: agentbase.cpp:1010
virtual ~Observer()
Destroys the observer instance.
Definition: agentbase.cpp:69
QCommandLineOption addVersionOption()
void configurationDialogRejected()
This signal is emitted whenever the user has rejected the configuration dialog.
static Session * defaultSession()
Returns the default session for this thread.
void itemsUnlinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection)
This signal is emitted if a reference to items is removed from a virtual collection.
virtual void itemsUnlinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection)
Reimplement to handle batch notifications about items unlinking.
Definition: agentbase.cpp:221
void timeout()
bool isEmpty() const const
static QString addNamespace(const QString &string)
Adds the multi-instance namespace to string if required (with '_' as separator).
virtual QString statusMessage() const
This method returns an i18n'ed description of the current status code.
Definition: agentbase.cpp:953
void reloadConfiguration()
Emitted if another application has changed the agent's configuration remotely and called AgentInstanc...
Observer()
Creates an observer instance.
Definition: agentbase.cpp:65
virtual int status() const
This method returns the current status code of the agent.
Definition: agentbase.cpp:946
QString value(const QString &optionName) const const
void setAgentName(const QString &name)
This method is used to set the name of the agent.
Definition: agentbase.cpp:1249
void configurationDialogAccepted()
This signal is emitted whenever the user has accepted the configuration dialog.
BC extension of Observer with support for monitoring item and collection moves.
Definition: agentbase.h:237
UniqueConnection
virtual void relationAdded(const Akonadi::Relation &relation)
Reimplement to handle relations being added.
Definition: agentbase.cpp:279
virtual void tagRemoved(const Akonadi::Tag &tag)
Reimplement to handle tags removal.
Definition: agentbase.cpp:255
The base class for all Akonadi agents and resources.
Definition: agentbase.h:72
AgentBase(const QString &id)
Creates an agent base.
void setApplicationName(const QString &application)
bool isSet(const QString &name) const const
void itemMoved(const Akonadi::Item &item, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination)
This signal is emitted if a monitored item has been moved between two collections.
const QDBusMessage & message() const const
virtual void aboutToQuit()
This method is called whenever the agent application is about to quit.
Definition: agentbase.cpp:1147
BC extension of ObserverV2 with support for batch operations.
Definition: agentbase.h:301
QDBusConnectionInterface * interface() const const
QString & remove(int position, int n)
void itemChanged(const Akonadi::Item &item, const QSet< QByteArray > &partIdentifiers)
This signal is emitted if a monitored item has changed, e.g.
virtual void collectionChanged(const Akonadi::Collection &collection)
Reimplement to handle changes to existing collections.
Definition: agentbase.cpp:108
void itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection)
This signal is emitted if an item has been added to a monitored collection in the Akonadi storage.
virtual void itemsRelationsChanged(const Akonadi::Item::List &items, const Akonadi::Relation::List &addedRelations, const Akonadi::Relation::List &removedRelations)
Reimplement to handled relations changing on items.
Definition: agentbase.cpp:301
void error(const QString &message)
This signal shall be used to report errors.
void collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &source, const Akonadi::Collection &destination)
This signals is emitted if a monitored collection has been moved.
@ Broken
The agent encountered an error state.
Definition: agentbase.h:424
QString identifier() const
Returns the instance identifier of this agent.
Definition: agentbase.cpp:1244
virtual void collectionRemoved(const Akonadi::Collection &collection)
Reimplement to handle deletion of collections.
Definition: agentbase.cpp:116
QDBusMessage::MessageType type() const const
virtual void tagAdded(const Akonadi::Tag &tag)
Reimplement to handle tags additions.
Definition: agentbase.cpp:233
ChangeRecorder * changeRecorder() const
Returns the Akonadi::ChangeRecorder object used for monitoring.
Definition: agentbase.cpp:1289
virtual void itemMoved(const Akonadi::Item &item, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination)
Reimplement to handle item moves.
Definition: agentbase.cpp:124
virtual void collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent)
Reimplement to handle adding of new collections.
Definition: agentbase.cpp:99
void setNeedsNetwork(bool needsNetwork)
Sets whether the agent needs network or not.
Definition: agentbase.cpp:981
virtual void collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination)
Reimplement to handle collection moves.
Definition: agentbase.cpp:156
void itemUnlinked(const Akonadi::Item &item, const Akonadi::Collection &collection)
This signal is emitted if a reference to an item is removed from a virtual collection.
Q_SCRIPTABLE Q_NOREPLY void abort()
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void setTemporaryOffline(int makeOnlineInSeconds=300)
Sets the agent offline but will make it online again after a given time.
Definition: agentbase.cpp:1023
static AgentManager * self()
Returns the global instance of the agent manager.
Observer that adds support for item tagging.
Definition: agentbase.h:360
virtual void cleanup()
This method is called when the agent is removed from the system, so it can do some cleanup stuff.
Definition: agentbase.cpp:1153
bool addOption(const QCommandLineOption &option)
virtual void itemChanged(const Akonadi::Item &item, const QSet< QByteArray > &partIdentifiers)
Reimplement to handle changes to existing items.
Definition: agentbase.cpp:82
void onlineStateChanged(bool isOnline)
void abortRequested()
Emitted when another application has remotely asked the agent to abort its current operation.
QString agentName() const
Returns the name of the agent.
Definition: agentbase.cpp:1273
static void setApplicationDomain(const char *domain)
static QString agentConfigFilePath(const QString &identifier)
Returns absolute path to configuration file of an agent identified by given identifier.
QCommandLineOption addHelpOption()
virtual void itemsFlagsChanged(const Akonadi::Item::List &items, const QSet< QByteArray > &addedFlags, const QSet< QByteArray > &removedFlags)
Reimplement to handle changes in flags of existing items.
Definition: agentbase.cpp:172
virtual void collectionChanged(const Akonadi::Collection &collection, const QSet< QByteArray > &changedAttributes)
Reimplement to handle changes to existing collections.
Definition: agentbase.cpp:166
@ Running
The agent is working on something.
Definition: agentbase.h:423
QObject * parent() const const
void tagChanged(const Akonadi::Tag &tag)
This signal is emitted if a monitored tag is changed on the server.
Represents a PIM item stored in Akonadi storage.
Definition: item.h:105
Q_D(Todo)
void warning(const QString &message)
This signal shall be used to report warnings.
virtual void itemUnlinked(const Akonadi::Item &item, const Akonadi::Collection &collection)
Reimplement to handle item unlinking.
Definition: agentbase.cpp:145
virtual int progress() const
This method returns the current progress of the agent in percentage.
Definition: agentbase.cpp:960
virtual void itemRemoved(const Akonadi::Item &item)
Reimplement to handle deletion of items.
Definition: agentbase.cpp:91
Helper integration between Akonadi and Qt.
QString name() const
Returns the i18n'ed name of the agent type.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Mar 21 2023 03:56:10 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.