File indexing completed on 2025-01-05 04:46:24
0001 /* 0002 SPDX-FileCopyrightText: 2006 Till Adam <adam@kde.org> 0003 SPDX-FileCopyrightText: 2007 Volker Krause <vkrause@kde.org> 0004 SPDX-FileCopyrightText: 2007 Bruno Virlet <bruno.virlet@gmail.com> 0005 SPDX-FileCopyrightText: 2008 Kevin Krammer <kevin.krammer@gmx.at> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "agentbase.h" 0011 #include "agentbase_p.h" 0012 0013 #include "agentconfigurationdialog.h" 0014 #include "agentmanager.h" 0015 #include "akonadifull-version.h" 0016 #include "changerecorder.h" 0017 #include "controladaptor.h" 0018 #include "itemfetchjob.h" 0019 #include "monitor_p.h" 0020 #include "private/standarddirs_p.h" 0021 #include "servermanager_p.h" 0022 #include "session.h" 0023 #include "session_p.h" 0024 #include "statusadaptor.h" 0025 0026 #include "akonadiagentbase_debug.h" 0027 0028 #include <KLocalizedString> 0029 0030 #include <KAboutData> 0031 0032 #include <QCommandLineParser> 0033 #include <QNetworkInformation> 0034 #include <QPointer> 0035 #include <QSettings> 0036 #include <QTimer> 0037 0038 #include <QStandardPaths> 0039 #include <signal.h> 0040 #include <stdlib.h> 0041 #if defined __GLIBC__ 0042 #include <malloc.h> // for dumping memory information 0043 #endif 0044 0045 #ifdef Q_OS_WIN 0046 #include <Windows.h> 0047 #endif 0048 0049 #include <chrono> 0050 #include <thread> 0051 0052 using namespace Akonadi; 0053 using namespace std::chrono_literals; 0054 static AgentBase *sAgentBase = nullptr; 0055 0056 AgentBase::Observer::Observer() 0057 { 0058 } 0059 0060 AgentBase::Observer::~Observer() 0061 { 0062 } 0063 0064 void AgentBase::Observer::itemAdded(const Item &item, const Collection &collection) 0065 { 0066 Q_UNUSED(item) 0067 Q_UNUSED(collection) 0068 if (sAgentBase) { 0069 sAgentBase->d_ptr->changeProcessed(); 0070 } 0071 } 0072 0073 void AgentBase::Observer::itemChanged(const Item &item, const QSet<QByteArray> &partIdentifiers) 0074 { 0075 Q_UNUSED(item) 0076 Q_UNUSED(partIdentifiers) 0077 if (sAgentBase) { 0078 sAgentBase->d_ptr->changeProcessed(); 0079 } 0080 } 0081 0082 void AgentBase::Observer::itemRemoved(const Item &item) 0083 { 0084 Q_UNUSED(item) 0085 if (sAgentBase) { 0086 sAgentBase->d_ptr->changeProcessed(); 0087 } 0088 } 0089 0090 void AgentBase::Observer::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) 0091 { 0092 Q_UNUSED(collection) 0093 Q_UNUSED(parent) 0094 if (sAgentBase) { 0095 sAgentBase->d_ptr->changeProcessed(); 0096 } 0097 } 0098 0099 void AgentBase::Observer::collectionChanged(const Collection &collection) 0100 { 0101 Q_UNUSED(collection) 0102 if (sAgentBase) { 0103 sAgentBase->d_ptr->changeProcessed(); 0104 } 0105 } 0106 0107 void AgentBase::Observer::collectionRemoved(const Collection &collection) 0108 { 0109 Q_UNUSED(collection) 0110 if (sAgentBase) { 0111 sAgentBase->d_ptr->changeProcessed(); 0112 } 0113 } 0114 0115 void AgentBase::ObserverV2::itemMoved(const Akonadi::Item &item, const Akonadi::Collection &source, const Akonadi::Collection &dest) 0116 { 0117 Q_UNUSED(item) 0118 Q_UNUSED(source) 0119 Q_UNUSED(dest) 0120 if (sAgentBase) { 0121 sAgentBase->d_ptr->changeProcessed(); 0122 } 0123 } 0124 0125 void AgentBase::ObserverV2::itemLinked(const Akonadi::Item &item, const Akonadi::Collection &collection) 0126 { 0127 Q_UNUSED(item) 0128 Q_UNUSED(collection) 0129 if (sAgentBase) { 0130 // not implementation, let's disconnect the signal to enable optimizations in Monitor 0131 QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemLinked, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemLinked); 0132 sAgentBase->d_ptr->changeProcessed(); 0133 } 0134 } 0135 0136 void AgentBase::ObserverV2::itemUnlinked(const Akonadi::Item &item, const Akonadi::Collection &collection) 0137 { 0138 Q_UNUSED(item) 0139 Q_UNUSED(collection) 0140 if (sAgentBase) { 0141 // not implementation, let's disconnect the signal to enable optimizations in Monitor 0142 QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemUnlinked, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemUnlinked); 0143 sAgentBase->d_ptr->changeProcessed(); 0144 } 0145 } 0146 0147 void AgentBase::ObserverV2::collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &source, const Akonadi::Collection &dest) 0148 { 0149 Q_UNUSED(collection) 0150 Q_UNUSED(source) 0151 Q_UNUSED(dest) 0152 if (sAgentBase) { 0153 sAgentBase->d_ptr->changeProcessed(); 0154 } 0155 } 0156 0157 void AgentBase::ObserverV2::collectionChanged(const Akonadi::Collection &collection, const QSet<QByteArray> &changedAttributes) 0158 { 0159 Q_UNUSED(changedAttributes) 0160 collectionChanged(collection); 0161 } 0162 0163 void AgentBase::ObserverV3::itemsFlagsChanged(const Akonadi::Item::List &items, const QSet<QByteArray> &addedFlags, const QSet<QByteArray> &removedFlags) 0164 { 0165 Q_UNUSED(items) 0166 Q_UNUSED(addedFlags) 0167 Q_UNUSED(removedFlags) 0168 0169 if (sAgentBase) { 0170 // not implementation, let's disconnect the signal to enable optimizations in Monitor 0171 QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsFlagsChanged, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsFlagsChanged); 0172 sAgentBase->d_ptr->changeProcessed(); 0173 } 0174 } 0175 0176 void AgentBase::ObserverV3::itemsMoved(const Akonadi::Item::List &items, const Collection &sourceCollection, const Collection &destinationCollection) 0177 { 0178 Q_UNUSED(items) 0179 Q_UNUSED(sourceCollection) 0180 Q_UNUSED(destinationCollection) 0181 0182 if (sAgentBase) { 0183 // not implementation, let's disconnect the signal to enable optimizations in Monitor 0184 QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsMoved, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsMoved); 0185 sAgentBase->d_ptr->changeProcessed(); 0186 } 0187 } 0188 0189 void AgentBase::ObserverV3::itemsRemoved(const Akonadi::Item::List &items) 0190 { 0191 Q_UNUSED(items) 0192 0193 if (sAgentBase) { 0194 // not implementation, let's disconnect the signal to enable optimizations in Monitor 0195 QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsRemoved, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsRemoved); 0196 sAgentBase->d_ptr->changeProcessed(); 0197 } 0198 } 0199 0200 void AgentBase::ObserverV3::itemsLinked(const Akonadi::Item::List &items, const Collection &collection) 0201 { 0202 Q_UNUSED(items) 0203 Q_UNUSED(collection) 0204 0205 if (sAgentBase) { 0206 // not implementation, let's disconnect the signal to enable optimizations in Monitor 0207 QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsLinked, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsLinked); 0208 sAgentBase->d_ptr->changeProcessed(); 0209 } 0210 } 0211 0212 void AgentBase::ObserverV3::itemsUnlinked(const Akonadi::Item::List &items, const Collection &collection) 0213 { 0214 Q_UNUSED(items) 0215 Q_UNUSED(collection) 0216 0217 if (sAgentBase) { 0218 // not implementation, let's disconnect the signal to enable optimizations in Monitor 0219 QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsUnlinked, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsUnlinked); 0220 sAgentBase->d_ptr->changeProcessed(); 0221 } 0222 } 0223 0224 void AgentBase::ObserverV4::tagAdded(const Tag &tag) 0225 { 0226 Q_UNUSED(tag) 0227 0228 if (sAgentBase) { 0229 // not implementation, let's disconnect the signal to enable optimization in Monitor 0230 QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::tagAdded, sAgentBase->d_ptr.get(), &AgentBasePrivate::tagAdded); 0231 sAgentBase->d_ptr->changeProcessed(); 0232 } 0233 } 0234 0235 void AgentBase::ObserverV4::tagChanged(const Tag &tag) 0236 { 0237 Q_UNUSED(tag) 0238 0239 if (sAgentBase) { 0240 // not implementation, let's disconnect the signal to enable optimization in Monitor 0241 QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::tagChanged, sAgentBase->d_ptr.get(), &AgentBasePrivate::tagChanged); 0242 sAgentBase->d_ptr->changeProcessed(); 0243 } 0244 } 0245 0246 void AgentBase::ObserverV4::tagRemoved(const Tag &tag) 0247 { 0248 Q_UNUSED(tag) 0249 0250 if (sAgentBase) { 0251 // not implementation, let's disconnect the signal to enable optimization in Monitor 0252 QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::tagRemoved, sAgentBase->d_ptr.get(), &AgentBasePrivate::tagRemoved); 0253 sAgentBase->d_ptr->changeProcessed(); 0254 } 0255 } 0256 0257 void AgentBase::ObserverV4::itemsTagsChanged(const Item::List &items, const QSet<Tag> &addedTags, const QSet<Tag> &removedTags) 0258 { 0259 Q_UNUSED(items) 0260 Q_UNUSED(addedTags) 0261 Q_UNUSED(removedTags) 0262 0263 if (sAgentBase) { 0264 // not implementation, let's disconnect the signal to enable optimization in Monitor 0265 QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsTagsChanged, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsTagsChanged); 0266 sAgentBase->d_ptr->changeProcessed(); 0267 } 0268 } 0269 0270 void AgentBase::ObserverV4::relationAdded(const Akonadi::Relation &relation) 0271 { 0272 Q_UNUSED(relation) 0273 0274 if (sAgentBase) { 0275 // not implementation, let's disconnect the signal to enable optimization in Monitor 0276 QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::relationAdded, sAgentBase->d_ptr.get(), &AgentBasePrivate::relationAdded); 0277 sAgentBase->d_ptr->changeProcessed(); 0278 } 0279 } 0280 0281 void AgentBase::ObserverV4::relationRemoved(const Akonadi::Relation &relation) 0282 { 0283 Q_UNUSED(relation) 0284 0285 if (sAgentBase) { 0286 // not implementation, let's disconnect the signal to enable optimization in Monitor 0287 QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::relationRemoved, sAgentBase->d_ptr.get(), &AgentBasePrivate::relationRemoved); 0288 sAgentBase->d_ptr->changeProcessed(); 0289 } 0290 } 0291 0292 void AgentBase::ObserverV4::itemsRelationsChanged(const Akonadi::Item::List &items, 0293 const Akonadi::Relation::List &addedRelations, 0294 const Akonadi::Relation::List &removedRelations) 0295 { 0296 Q_UNUSED(items) 0297 Q_UNUSED(addedRelations) 0298 Q_UNUSED(removedRelations) 0299 0300 if (sAgentBase) { 0301 // not implementation, let's disconnect the signal to enable optimization in Monitor 0302 disconnect(sAgentBase->changeRecorder(), &Monitor::itemsRelationsChanged, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsRelationsChanged); 0303 sAgentBase->d_ptr->changeProcessed(); 0304 } 0305 } 0306 0307 /// @cond PRIVATE 0308 0309 AgentBasePrivate::AgentBasePrivate(AgentBase *parent) 0310 : q_ptr(parent) 0311 , mStatusCode(AgentBase::Idle) 0312 , mProgress(0) 0313 , mNeedsNetwork(false) 0314 , mOnline(false) 0315 , mDesiredOnlineState(false) 0316 , mPendingQuit(false) 0317 , mSettings(nullptr) 0318 , mChangeRecorder(nullptr) 0319 , mTracer(nullptr) 0320 , mObserver(nullptr) 0321 , mPowerInterface(nullptr) 0322 , mTemporaryOfflineTimer(nullptr) 0323 , mEventLoopLocker(nullptr) 0324 { 0325 Internal::setClientType(Internal::Agent); 0326 } 0327 0328 AgentBasePrivate::~AgentBasePrivate() 0329 { 0330 mChangeRecorder->setConfig(nullptr); 0331 delete mSettings; 0332 } 0333 0334 void AgentBasePrivate::init() 0335 { 0336 Q_Q(AgentBase); 0337 /** 0338 * Create a default session for this process. 0339 */ 0340 SessionPrivate::createDefaultSession(mId.toLatin1()); 0341 0342 mTracer = 0343 new org::freedesktop::Akonadi::Tracer(ServerManager::serviceName(ServerManager::Server), QStringLiteral("/tracing"), QDBusConnection::sessionBus(), q); 0344 0345 new Akonadi__ControlAdaptor(q); 0346 new Akonadi__StatusAdaptor(q); 0347 if (!QDBusConnection::sessionBus().registerObject(QStringLiteral("/"), q, QDBusConnection::ExportAdaptors)) { 0348 Q_EMIT q->error(i18n("Unable to register object at dbus: %1", QDBusConnection::sessionBus().lastError().message())); 0349 } 0350 0351 mSettings = new QSettings(ServerManager::agentConfigFilePath(mId), QSettings::IniFormat); 0352 0353 mChangeRecorder = new ChangeRecorder(q); 0354 mChangeRecorder->setObjectName(QLatin1StringView("AgentBaseChangeRecorder")); 0355 mChangeRecorder->ignoreSession(Session::defaultSession()); 0356 mChangeRecorder->itemFetchScope().setCacheOnly(true); 0357 mChangeRecorder->setConfig(mSettings); 0358 0359 mDesiredOnlineState = mSettings->value(QStringLiteral("Agent/DesiredOnlineState"), true).toBool(); 0360 mOnline = mDesiredOnlineState; 0361 0362 // reinitialize the status message now that online state is available 0363 mStatusMessage = defaultReadyMessage(); 0364 0365 mName = mSettings->value(QStringLiteral("Agent/Name")).toString(); 0366 if (mName.isEmpty()) { 0367 mName = mSettings->value(QStringLiteral("Resource/Name")).toString(); 0368 if (!mName.isEmpty()) { 0369 mSettings->remove(QStringLiteral("Resource/Name")); 0370 mSettings->setValue(QStringLiteral("Agent/Name"), mName); 0371 } 0372 } 0373 0374 connect(mChangeRecorder, &Monitor::itemAdded, this, &AgentBasePrivate::itemAdded); 0375 connect(mChangeRecorder, &Monitor::itemChanged, this, &AgentBasePrivate::itemChanged); 0376 connect(mChangeRecorder, &Monitor::collectionAdded, this, &AgentBasePrivate::collectionAdded); 0377 connect(mChangeRecorder, 0378 qOverload<const Collection &>(&ChangeRecorder::collectionChanged), 0379 this, 0380 qOverload<const Collection &>(&AgentBasePrivate::collectionChanged)); 0381 connect(mChangeRecorder, 0382 qOverload<const Collection &, const QSet<QByteArray> &>(&ChangeRecorder::collectionChanged), 0383 this, 0384 qOverload<const Collection &, const QSet<QByteArray> &>(&AgentBasePrivate::collectionChanged)); 0385 connect(mChangeRecorder, &Monitor::collectionMoved, this, &AgentBasePrivate::collectionMoved); 0386 connect(mChangeRecorder, &Monitor::collectionRemoved, this, &AgentBasePrivate::collectionRemoved); 0387 connect(mChangeRecorder, &Monitor::collectionSubscribed, this, &AgentBasePrivate::collectionSubscribed); 0388 connect(mChangeRecorder, &Monitor::collectionUnsubscribed, this, &AgentBasePrivate::collectionUnsubscribed); 0389 0390 connect(q, qOverload<int, const QString &>(&AgentBase::status), this, &AgentBasePrivate::slotStatus); 0391 connect(q, &AgentBase::percent, this, &AgentBasePrivate::slotPercent); 0392 connect(q, &AgentBase::warning, this, &AgentBasePrivate::slotWarning); 0393 connect(q, &AgentBase::error, this, &AgentBasePrivate::slotError); 0394 0395 mPowerInterface = new QDBusInterface(QStringLiteral("org.kde.Solid.PowerManagement"), 0396 QStringLiteral("/org/kde/Solid/PowerManagement/Actions/SuspendSession"), 0397 QStringLiteral("org.kde.Solid.PowerManagement.Actions.SuspendSession"), 0398 QDBusConnection::sessionBus(), 0399 this); 0400 if (mPowerInterface->isValid()) { 0401 connect(mPowerInterface, SIGNAL(resumingFromSuspend()), this, SLOT(slotResumedFromSuspend())); // clazy:exclude=old-style-connect 0402 } else { 0403 delete mPowerInterface; 0404 mPowerInterface = nullptr; 0405 } 0406 0407 // Use reference counting to allow agents to finish internal jobs when the 0408 // agent is stopped. 0409 mEventLoopLocker = new QEventLoopLocker(); 0410 0411 mResourceTypeName = AgentManager::self()->instance(mId).type().name(); 0412 setProgramName(); 0413 0414 QTimer::singleShot(0s, q, [this] { 0415 delayedInit(); 0416 }); 0417 } 0418 0419 void AgentBasePrivate::delayedInit() 0420 { 0421 Q_Q(AgentBase); 0422 0423 const QString serviceId = ServerManager::agentServiceName(ServerManager::Agent, mId); 0424 if (!QDBusConnection::sessionBus().registerService(serviceId)) { 0425 qCCritical(AKONADIAGENTBASE_LOG) << "Unable to register service" << serviceId << "at dbus:" << QDBusConnection::sessionBus().lastError().message(); 0426 } 0427 q->setOnlineInternal(mDesiredOnlineState); 0428 0429 QDBusConnection::sessionBus().registerObject(QStringLiteral("/Debug"), this, QDBusConnection::ExportScriptableSlots); 0430 } 0431 0432 void AgentBasePrivate::setProgramName() 0433 { 0434 // ugly, really ugly, if you find another solution, change it and blame me for this code (Andras) 0435 QString programName = mResourceTypeName; 0436 if (!mName.isEmpty()) { 0437 programName = i18nc("Name and type of Akonadi resource", "%1 of type %2", mName, mResourceTypeName); 0438 } 0439 0440 QGuiApplication::setApplicationDisplayName(programName); 0441 } 0442 0443 void AgentBasePrivate::itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) 0444 { 0445 if (mObserver) { 0446 mObserver->itemAdded(item, collection); 0447 } else { 0448 changeProcessed(); 0449 } 0450 } 0451 0452 void AgentBasePrivate::itemChanged(const Akonadi::Item &item, const QSet<QByteArray> &partIdentifiers) 0453 { 0454 if (mObserver) { 0455 mObserver->itemChanged(item, partIdentifiers); 0456 } else { 0457 changeProcessed(); 0458 } 0459 } 0460 0461 void AgentBasePrivate::itemMoved(const Akonadi::Item &item, const Akonadi::Collection &source, const Akonadi::Collection &dest) 0462 { 0463 auto observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver); 0464 if (mObserver) { 0465 // inter-resource moves, requires we know which resources the source and destination are in though 0466 if (!source.resource().isEmpty() && !dest.resource().isEmpty()) { 0467 if (source.resource() != dest.resource()) { 0468 if (source.resource() == q_ptr->identifier()) { // moved away from us 0469 Akonadi::Item i(item); 0470 i.setParentCollection(source); 0471 mObserver->itemRemoved(i); 0472 } else if (dest.resource() == q_ptr->identifier()) { // moved to us 0473 mObserver->itemAdded(item, dest); 0474 } else if (observer2) { 0475 observer2->itemMoved(item, source, dest); 0476 } else { 0477 // not for us, not sure if we should get here at all 0478 changeProcessed(); 0479 } 0480 return; 0481 } 0482 } 0483 // intra-resource move 0484 if (observer2) { 0485 observer2->itemMoved(item, source, dest); 0486 } else { 0487 // ### we cannot just call itemRemoved here as this will already trigger changeProcessed() 0488 // so, just itemAdded() is good enough as no resource can have implemented intra-resource moves anyway 0489 // without using ObserverV2 0490 mObserver->itemAdded(item, dest); 0491 // mObserver->itemRemoved( item ); 0492 } 0493 } 0494 } 0495 0496 void AgentBasePrivate::itemRemoved(const Akonadi::Item &item) 0497 { 0498 if (mObserver) { 0499 mObserver->itemRemoved(item); 0500 } else { 0501 changeProcessed(); 0502 } 0503 } 0504 0505 void AgentBasePrivate::itemLinked(const Akonadi::Item &item, const Akonadi::Collection &collection) 0506 { 0507 auto observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver); 0508 if (observer2) { 0509 observer2->itemLinked(item, collection); 0510 } else { 0511 changeProcessed(); 0512 } 0513 } 0514 0515 void AgentBasePrivate::itemUnlinked(const Akonadi::Item &item, const Akonadi::Collection &collection) 0516 { 0517 auto observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver); 0518 if (observer2) { 0519 observer2->itemUnlinked(item, collection); 0520 } else { 0521 changeProcessed(); 0522 } 0523 } 0524 0525 void AgentBasePrivate::itemsFlagsChanged(const Akonadi::Item::List &items, const QSet<QByteArray> &addedFlags, const QSet<QByteArray> &removedFlags) 0526 { 0527 auto observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver); 0528 if (observer3) { 0529 observer3->itemsFlagsChanged(items, addedFlags, removedFlags); 0530 } else { 0531 Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available"); 0532 } 0533 } 0534 0535 void AgentBasePrivate::itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &source, const Akonadi::Collection &destination) 0536 { 0537 auto observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver); 0538 if (observer3) { 0539 observer3->itemsMoved(items, source, destination); 0540 } else { 0541 Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available"); 0542 } 0543 } 0544 0545 void AgentBasePrivate::itemsRemoved(const Akonadi::Item::List &items) 0546 { 0547 auto observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver); 0548 if (observer3) { 0549 observer3->itemsRemoved(items); 0550 } else { 0551 Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available"); 0552 } 0553 } 0554 0555 void AgentBasePrivate::itemsLinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection) 0556 { 0557 if (!mObserver) { 0558 changeProcessed(); 0559 return; 0560 } 0561 0562 auto observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver); 0563 if (observer3) { 0564 observer3->itemsLinked(items, collection); 0565 } else { 0566 Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available"); 0567 } 0568 } 0569 0570 void AgentBasePrivate::itemsUnlinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection) 0571 { 0572 if (!mObserver) { 0573 changeProcessed(); 0574 return; 0575 } 0576 0577 auto observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver); 0578 if (observer3) { 0579 observer3->itemsUnlinked(items, collection); 0580 } else { 0581 Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available"); 0582 } 0583 } 0584 0585 void AgentBasePrivate::tagAdded(const Akonadi::Tag &tag) 0586 { 0587 auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver); 0588 if (observer4) { 0589 observer4->tagAdded(tag); 0590 } else { 0591 changeProcessed(); 0592 } 0593 } 0594 0595 void AgentBasePrivate::tagChanged(const Akonadi::Tag &tag) 0596 { 0597 auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver); 0598 if (observer4) { 0599 observer4->tagChanged(tag); 0600 } else { 0601 changeProcessed(); 0602 } 0603 } 0604 0605 void AgentBasePrivate::tagRemoved(const Akonadi::Tag &tag) 0606 { 0607 auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver); 0608 if (observer4) { 0609 observer4->tagRemoved(tag); 0610 } else { 0611 changeProcessed(); 0612 } 0613 } 0614 0615 void AgentBasePrivate::itemsTagsChanged(const Akonadi::Item::List &items, const QSet<Akonadi::Tag> &addedTags, const QSet<Akonadi::Tag> &removedTags) 0616 { 0617 auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver); 0618 if (observer4) { 0619 observer4->itemsTagsChanged(items, addedTags, removedTags); 0620 } else { 0621 changeProcessed(); 0622 } 0623 } 0624 0625 void AgentBasePrivate::relationAdded(const Akonadi::Relation &relation) 0626 { 0627 auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver); 0628 if (observer4) { 0629 observer4->relationAdded(relation); 0630 } else { 0631 changeProcessed(); 0632 } 0633 } 0634 0635 void AgentBasePrivate::relationRemoved(const Akonadi::Relation &relation) 0636 { 0637 auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver); 0638 if (observer4) { 0639 observer4->relationRemoved(relation); 0640 } else { 0641 changeProcessed(); 0642 } 0643 } 0644 0645 void AgentBasePrivate::itemsRelationsChanged(const Akonadi::Item::List &items, 0646 const Akonadi::Relation::List &addedRelations, 0647 const Akonadi::Relation::List &removedRelations) 0648 { 0649 auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver); 0650 if (observer4) { 0651 observer4->itemsRelationsChanged(items, addedRelations, removedRelations); 0652 } else { 0653 changeProcessed(); 0654 } 0655 } 0656 0657 void AgentBasePrivate::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) 0658 { 0659 if (mObserver) { 0660 mObserver->collectionAdded(collection, parent); 0661 } else { 0662 changeProcessed(); 0663 } 0664 } 0665 0666 void AgentBasePrivate::collectionChanged(const Akonadi::Collection &collection) 0667 { 0668 auto observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver); 0669 if (mObserver && observer2 == nullptr) { // For ObserverV2 we use the variant with the part identifiers 0670 mObserver->collectionChanged(collection); 0671 } else if (!mObserver) { 0672 changeProcessed(); 0673 } 0674 } 0675 0676 void AgentBasePrivate::collectionChanged(const Akonadi::Collection &collection, const QSet<QByteArray> &changedAttributes) 0677 { 0678 auto observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver); 0679 if (observer2) { 0680 observer2->collectionChanged(collection, changedAttributes); 0681 } else { 0682 changeProcessed(); 0683 } 0684 } 0685 0686 void AgentBasePrivate::collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &source, const Akonadi::Collection &dest) 0687 { 0688 auto observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver); 0689 if (observer2) { 0690 observer2->collectionMoved(collection, source, dest); 0691 } else if (mObserver) { 0692 // ### we cannot just call collectionRemoved here as this will already trigger changeProcessed() 0693 // so, just collectionAdded() is good enough as no resource can have implemented intra-resource moves anyway 0694 // without using ObserverV2 0695 mObserver->collectionAdded(collection, dest); 0696 } else { 0697 changeProcessed(); 0698 } 0699 } 0700 0701 void AgentBasePrivate::collectionRemoved(const Akonadi::Collection &collection) 0702 { 0703 if (mObserver) { 0704 mObserver->collectionRemoved(collection); 0705 } else { 0706 changeProcessed(); 0707 } 0708 } 0709 0710 void AgentBasePrivate::collectionSubscribed(const Akonadi::Collection &collection, const Akonadi::Collection &parent) 0711 { 0712 Q_UNUSED(collection) 0713 Q_UNUSED(parent) 0714 changeProcessed(); 0715 } 0716 0717 void AgentBasePrivate::collectionUnsubscribed(const Akonadi::Collection &collection) 0718 { 0719 Q_UNUSED(collection) 0720 changeProcessed(); 0721 } 0722 0723 void AgentBasePrivate::changeProcessed() 0724 { 0725 mChangeRecorder->changeProcessed(); 0726 QTimer::singleShot(0s, mChangeRecorder, &ChangeRecorder::replayNext); 0727 } 0728 0729 void AgentBasePrivate::slotStatus(int status, const QString &message) 0730 { 0731 mStatusMessage = message; 0732 mStatusCode = 0; 0733 0734 switch (status) { 0735 case AgentBase::Idle: 0736 if (mStatusMessage.isEmpty()) { 0737 mStatusMessage = defaultReadyMessage(); 0738 } 0739 0740 mStatusCode = 0; 0741 break; 0742 case AgentBase::Running: 0743 if (mStatusMessage.isEmpty()) { 0744 mStatusMessage = defaultSyncingMessage(); 0745 } 0746 0747 mStatusCode = 1; 0748 break; 0749 case AgentBase::Broken: 0750 if (mStatusMessage.isEmpty()) { 0751 mStatusMessage = defaultErrorMessage(); 0752 } 0753 0754 mStatusCode = 2; 0755 break; 0756 0757 case AgentBase::NotConfigured: 0758 if (mStatusMessage.isEmpty()) { 0759 mStatusMessage = defaultUnconfiguredMessage(); 0760 } 0761 0762 mStatusCode = 3; 0763 break; 0764 0765 default: 0766 Q_ASSERT(!"Unknown status passed"); 0767 break; 0768 } 0769 } 0770 0771 void AgentBasePrivate::slotPercent(int progress) 0772 { 0773 mProgress = progress; 0774 } 0775 0776 void AgentBasePrivate::slotWarning(const QString &message) 0777 { 0778 mTracer->warning(QStringLiteral("AgentBase(%1)").arg(mId), message); 0779 } 0780 0781 void AgentBasePrivate::slotError(const QString &message) 0782 { 0783 mTracer->error(QStringLiteral("AgentBase(%1)").arg(mId), message); 0784 } 0785 0786 void AgentBasePrivate::slotNetworkStatusChange(bool isOnline) 0787 { 0788 Q_UNUSED(isOnline) 0789 Q_Q(AgentBase); 0790 q->setOnlineInternal(mDesiredOnlineState); 0791 } 0792 0793 void AgentBasePrivate::slotResumedFromSuspend() 0794 { 0795 if (mNeedsNetwork) { 0796 slotNetworkStatusChange(QNetworkInformation::instance()->reachability() != QNetworkInformation::Reachability::Online); 0797 } 0798 } 0799 0800 void AgentBasePrivate::slotTemporaryOfflineTimeout() 0801 { 0802 Q_Q(AgentBase); 0803 q->setOnlineInternal(true); 0804 } 0805 0806 QString AgentBasePrivate::dumpNotificationListToString() const 0807 { 0808 return mChangeRecorder->dumpNotificationListToString(); 0809 } 0810 0811 void AgentBasePrivate::dumpMemoryInfo() const 0812 { 0813 // Send it to stdout, so we can debug user problems. 0814 // since you have to explicitly call this 0815 // it won't flood users with release builds. 0816 QTextStream stream(stdout); 0817 stream << dumpMemoryInfoToString(); 0818 } 0819 0820 QString AgentBasePrivate::dumpMemoryInfoToString() const 0821 { 0822 // man mallinfo for more info 0823 QString str; 0824 #if defined __GLIBC__ 0825 struct mallinfo mi; 0826 mi = mallinfo(); 0827 QTextStream stream(&str); 0828 stream << "Total non-mmapped bytes (arena): " << mi.arena << '\n' 0829 << "# of free chunks (ordblks): " << mi.ordblks << '\n' 0830 << "# of free fastbin blocks (smblks>: " << mi.smblks << '\n' 0831 << "# of mapped regions (hblks): " << mi.hblks << '\n' 0832 << "Bytes in mapped regions (hblkhd): " << mi.hblkhd << '\n' 0833 << "Max. total allocated space (usmblks): " << mi.usmblks << '\n' 0834 << "Free bytes held in fastbins (fsmblks):" << mi.fsmblks << '\n' 0835 << "Total allocated space (uordblks): " << mi.uordblks << '\n' 0836 << "Total free space (fordblks): " << mi.fordblks << '\n' 0837 << "Topmost releasable block (keepcost): " << mi.keepcost << '\n'; 0838 #else 0839 str = QLatin1StringView("mallinfo() not supported"); 0840 #endif 0841 return str; 0842 } 0843 0844 AgentBase::AgentBase(const QString &id) 0845 : d_ptr(new AgentBasePrivate(this)) 0846 { 0847 sAgentBase = this; 0848 d_ptr->mId = id; 0849 d_ptr->init(); 0850 } 0851 0852 AgentBase::AgentBase(AgentBasePrivate *d, const QString &id) 0853 : d_ptr(d) 0854 { 0855 sAgentBase = this; 0856 d_ptr->mId = id; 0857 d_ptr->init(); 0858 } 0859 0860 AgentBase::~AgentBase() = default; 0861 0862 void AgentBase::debugAgent(int argc, char **argv) 0863 { 0864 Q_UNUSED(argc) 0865 #ifdef Q_OS_WIN 0866 if (qEnvironmentVariableIsSet("AKONADI_DEBUG_WAIT")) { 0867 if (QByteArray(argv[0]).endsWith(QByteArray(qgetenv("AKONADI_DEBUG_WAIT") + QByteArrayLiteral(".exe")))) { 0868 while (!IsDebuggerPresent()) { 0869 std::this_thread::sleep_for(std::chrono::milliseconds(100)); 0870 } 0871 DebugBreak(); 0872 } 0873 } 0874 #else 0875 Q_UNUSED(argv) 0876 #endif 0877 } 0878 0879 QString AgentBase::parseArguments(int argc, char **argv) 0880 { 0881 Q_UNUSED(argc) 0882 0883 QCommandLineOption identifierOption(QStringLiteral("identifier"), i18n("Agent identifier"), QStringLiteral("argument")); 0884 QCommandLineParser parser; 0885 parser.addOption(identifierOption); 0886 parser.addHelpOption(); 0887 parser.addVersionOption(); 0888 parser.process(*qApp); 0889 parser.setApplicationDescription(i18n("Akonadi Agent")); 0890 0891 if (!parser.isSet(identifierOption)) { 0892 qCDebug(AKONADIAGENTBASE_LOG) << "Identifier argument missing"; 0893 exit(1); 0894 } 0895 0896 const QString identifier = parser.value(identifierOption); 0897 if (identifier.isEmpty()) { 0898 qCDebug(AKONADIAGENTBASE_LOG) << "Identifier argument is empty"; 0899 exit(1); 0900 } 0901 0902 QCoreApplication::setApplicationName(ServerManager::addNamespace(identifier)); 0903 QCoreApplication::setApplicationVersion(QStringLiteral(AKONADI_FULL_VERSION)); 0904 0905 const QFileInfo fi(QString::fromLocal8Bit(argv[0])); 0906 // strip off full path and possible .exe suffix 0907 const QString catalog = fi.baseName(); 0908 0909 auto translator = new QTranslator(qApp); 0910 translator->load(catalog); 0911 QCoreApplication::installTranslator(translator); 0912 0913 return identifier; 0914 } 0915 0916 /// @endcond 0917 0918 int AgentBase::init(AgentBase &r) 0919 { 0920 KLocalizedString::setApplicationDomain(QByteArrayLiteral("libakonadi6")); 0921 KAboutData::setApplicationData(r.aboutData()); 0922 return qApp->exec(); 0923 } 0924 0925 int AgentBase::status() const 0926 { 0927 Q_D(const AgentBase); 0928 0929 return d->mStatusCode; 0930 } 0931 0932 QString AgentBase::statusMessage() const 0933 { 0934 Q_D(const AgentBase); 0935 0936 return d->mStatusMessage; 0937 } 0938 0939 int AgentBase::progress() const 0940 { 0941 Q_D(const AgentBase); 0942 0943 return d->mProgress; 0944 } 0945 0946 QString AgentBase::progressMessage() const 0947 { 0948 Q_D(const AgentBase); 0949 0950 return d->mProgressMessage; 0951 } 0952 0953 bool AgentBase::isOnline() const 0954 { 0955 Q_D(const AgentBase); 0956 0957 return d->mOnline; 0958 } 0959 0960 void AgentBase::setNeedsNetwork(bool needsNetwork) 0961 { 0962 Q_D(AgentBase); 0963 if (d->mNeedsNetwork == needsNetwork) { 0964 return; 0965 } 0966 0967 d->mNeedsNetwork = needsNetwork; 0968 QNetworkInformation::load(QNetworkInformation::Feature::Reachability); 0969 connect(QNetworkInformation::instance(), &QNetworkInformation::reachabilityChanged, this, [this, d](auto reachability) { 0970 d->slotNetworkStatusChange(reachability == QNetworkInformation::Reachability::Online); 0971 }); 0972 } 0973 0974 void AgentBase::setOnline(bool state) 0975 { 0976 Q_D(AgentBase); 0977 0978 if (d->mPendingQuit) { 0979 return; 0980 } 0981 0982 d->mDesiredOnlineState = state; 0983 if (!d->mSettings) { 0984 d->mSettings = new QSettings(ServerManager::agentConfigFilePath(identifier()), QSettings::IniFormat); 0985 d->mSettings->setValue(QStringLiteral("Agent/Name"), agentName()); 0986 } 0987 d->mSettings->setValue(QStringLiteral("Agent/DesiredOnlineState"), state); 0988 setOnlineInternal(state); 0989 } 0990 0991 void AgentBase::setTemporaryOffline(int makeOnlineInSeconds) 0992 { 0993 Q_D(AgentBase); 0994 0995 // if not currently online, avoid bringing it online after the timeout 0996 if (!d->mOnline) { 0997 return; 0998 } 0999 1000 setOnlineInternal(false); 1001 1002 if (!d->mTemporaryOfflineTimer) { 1003 d->mTemporaryOfflineTimer = new QTimer(d); 1004 d->mTemporaryOfflineTimer->setSingleShot(true); 1005 connect(d->mTemporaryOfflineTimer, &QTimer::timeout, d, &AgentBasePrivate::slotTemporaryOfflineTimeout); 1006 } 1007 d->mTemporaryOfflineTimer->setInterval(std::chrono::seconds{makeOnlineInSeconds}); 1008 d->mTemporaryOfflineTimer->start(); 1009 } 1010 1011 void AgentBase::setOnlineInternal(bool state) 1012 { 1013 Q_D(AgentBase); 1014 if (state && d->mNeedsNetwork) { 1015 if (QNetworkInformation::instance()->reachability() != QNetworkInformation::Reachability::Online) { 1016 // Don't go online if the resource needs network but there is none 1017 state = false; 1018 } 1019 } 1020 d->mOnline = state; 1021 1022 if (d->mTemporaryOfflineTimer) { 1023 d->mTemporaryOfflineTimer->stop(); 1024 } 1025 1026 const QString newMessage = d->defaultReadyMessage(); 1027 if (d->mStatusMessage != newMessage && d->mStatusCode != AgentBase::Broken) { 1028 Q_EMIT status(d->mStatusCode, newMessage); 1029 } 1030 1031 doSetOnline(state); 1032 Q_EMIT onlineChanged(state); 1033 } 1034 1035 void AgentBase::doSetOnline(bool online) 1036 { 1037 Q_UNUSED(online) 1038 } 1039 1040 KAboutData AgentBase::aboutData() const 1041 { 1042 // akonadi_google_resource_1 -> org.kde.akonadi_google_resource 1043 const QString desktopEntry = QLatin1StringView("org.kde.") + qApp->applicationName().remove(QRegularExpression(QStringLiteral("_[0-9]+$"))); 1044 1045 KAboutData data(qApp->applicationName(), agentName(), qApp->applicationVersion()); 1046 data.setDesktopFileName(desktopEntry); 1047 return data; 1048 } 1049 1050 void AgentBase::configure(WId windowId) 1051 { 1052 Q_UNUSED(windowId) 1053 1054 // Fallback if the agent implements the new plugin-based configuration, 1055 // but someone calls the deprecated configure() method 1056 auto instance = Akonadi::AgentManager::self()->instance(identifier()); 1057 QPointer<AgentConfigurationDialog> dialog = new AgentConfigurationDialog(instance, nullptr); 1058 if (dialog->exec()) { 1059 Q_EMIT configurationDialogAccepted(); 1060 } else { 1061 Q_EMIT configurationDialogRejected(); 1062 } 1063 delete dialog; 1064 } 1065 1066 #ifdef Q_OS_WIN // krazy:exclude=cpp 1067 void AgentBase::configure(qlonglong windowId) 1068 { 1069 configure(static_cast<WId>(windowId)); 1070 } 1071 #endif 1072 1073 WId AgentBase::winIdForDialogs() const 1074 { 1075 const bool registered = QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.akonaditray")); 1076 if (!registered) { 1077 return 0; 1078 } 1079 1080 QDBusInterface dbus(QStringLiteral("org.freedesktop.akonaditray"), QStringLiteral("/Actions"), QStringLiteral("org.freedesktop.Akonadi.Tray")); 1081 const QDBusMessage reply = dbus.call(QStringLiteral("getWinId")); 1082 1083 if (reply.type() == QDBusMessage::ErrorMessage) { 1084 return 0; 1085 } 1086 1087 const WId winid = static_cast<WId>(reply.arguments().at(0).toLongLong()); 1088 1089 return winid; 1090 } 1091 1092 void AgentBase::quit() 1093 { 1094 Q_D(AgentBase); 1095 aboutToQuit(); 1096 1097 if (d->mSettings) { 1098 d->mChangeRecorder->setConfig(nullptr); 1099 d->mSettings->sync(); 1100 delete d->mSettings; 1101 d->mSettings = nullptr; 1102 } 1103 1104 delete d->mEventLoopLocker; 1105 d->mEventLoopLocker = nullptr; 1106 } 1107 1108 void AgentBase::aboutToQuit() 1109 { 1110 Q_D(AgentBase); 1111 d->mPendingQuit = true; 1112 } 1113 1114 void AgentBase::cleanup() 1115 { 1116 Q_D(AgentBase); 1117 // prevent the monitor from picking up deletion signals for our own data if we are a resource 1118 // and thus avoid that we kill our own data as last act before our own death 1119 d->mChangeRecorder->blockSignals(true); 1120 1121 aboutToQuit(); 1122 1123 const QString fileName = d->mSettings->fileName(); 1124 1125 /* 1126 * First destroy the settings object... 1127 */ 1128 d->mChangeRecorder->setConfig(nullptr); 1129 delete d->mSettings; 1130 d->mSettings = nullptr; 1131 1132 /* 1133 * ... then remove the file from hd. 1134 */ 1135 if (!QFile::remove(fileName)) { 1136 qCWarning(AKONADIAGENTBASE_LOG) << "Impossible to remove " << fileName; 1137 } 1138 1139 /* 1140 * ... and remove the changes file from hd. 1141 */ 1142 const QString changeDataFileName = fileName + QStringLiteral("_changes.dat"); 1143 if (!QFile::remove(changeDataFileName)) { 1144 qCWarning(AKONADIAGENTBASE_LOG) << "Impossible to remove " << changeDataFileName; 1145 } 1146 1147 /* 1148 * ... and also remove the agent configuration file if there is one. 1149 */ 1150 const QString configFile = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QLatin1Char('/') + config()->name(); 1151 if (!QFile::remove(configFile)) { 1152 qCWarning(AKONADIAGENTBASE_LOG) << "Impossible to remove config file " << configFile; 1153 } 1154 1155 delete d->mEventLoopLocker; 1156 d->mEventLoopLocker = nullptr; 1157 } 1158 1159 void AgentBase::registerObserver(Observer *observer) 1160 { 1161 Q_D(AgentBase); 1162 1163 // TODO in theory we should re-connect change recorder signals here that we disconnected previously 1164 d->mObserver = observer; 1165 1166 const bool hasObserverV3 = (dynamic_cast<AgentBase::ObserverV3 *>(d->mObserver) != nullptr); 1167 const bool hasObserverV4 = (dynamic_cast<AgentBase::ObserverV4 *>(d->mObserver) != nullptr); 1168 1169 disconnect(d->mChangeRecorder, &Monitor::tagAdded, d, &AgentBasePrivate::tagAdded); 1170 disconnect(d->mChangeRecorder, &Monitor::tagChanged, d, &AgentBasePrivate::tagChanged); 1171 disconnect(d->mChangeRecorder, &Monitor::tagRemoved, d, &AgentBasePrivate::tagRemoved); 1172 disconnect(d->mChangeRecorder, &Monitor::itemsTagsChanged, d, &AgentBasePrivate::itemsTagsChanged); 1173 disconnect(d->mChangeRecorder, &Monitor::itemsFlagsChanged, d, &AgentBasePrivate::itemsFlagsChanged); 1174 disconnect(d->mChangeRecorder, &Monitor::itemsMoved, d, &AgentBasePrivate::itemsMoved); 1175 disconnect(d->mChangeRecorder, &Monitor::itemsRemoved, d, &AgentBasePrivate::itemsRemoved); 1176 disconnect(d->mChangeRecorder, &Monitor::itemsLinked, d, &AgentBasePrivate::itemsLinked); 1177 disconnect(d->mChangeRecorder, &Monitor::itemsUnlinked, d, &AgentBasePrivate::itemsUnlinked); 1178 disconnect(d->mChangeRecorder, &Monitor::itemMoved, d, &AgentBasePrivate::itemMoved); 1179 disconnect(d->mChangeRecorder, &Monitor::itemRemoved, d, &AgentBasePrivate::itemRemoved); 1180 disconnect(d->mChangeRecorder, &Monitor::itemLinked, d, &AgentBasePrivate::itemLinked); 1181 disconnect(d->mChangeRecorder, &Monitor::itemUnlinked, d, &AgentBasePrivate::itemUnlinked); 1182 1183 if (hasObserverV4) { 1184 connect(d->mChangeRecorder, &Monitor::tagAdded, d, &AgentBasePrivate::tagAdded); 1185 connect(d->mChangeRecorder, &Monitor::tagChanged, d, &AgentBasePrivate::tagChanged); 1186 connect(d->mChangeRecorder, &Monitor::tagRemoved, d, &AgentBasePrivate::tagRemoved); 1187 connect(d->mChangeRecorder, &Monitor::itemsTagsChanged, d, &AgentBasePrivate::itemsTagsChanged); 1188 } 1189 1190 if (hasObserverV3) { 1191 connect(d->mChangeRecorder, &Monitor::itemsFlagsChanged, d, &AgentBasePrivate::itemsFlagsChanged); 1192 connect(d->mChangeRecorder, &Monitor::itemsMoved, d, &AgentBasePrivate::itemsMoved); 1193 connect(d->mChangeRecorder, &Monitor::itemsRemoved, d, &AgentBasePrivate::itemsRemoved); 1194 connect(d->mChangeRecorder, &Monitor::itemsLinked, d, &AgentBasePrivate::itemsLinked); 1195 connect(d->mChangeRecorder, &Monitor::itemsUnlinked, d, &AgentBasePrivate::itemsUnlinked); 1196 } else { 1197 // V2 - don't connect these if we have V3 1198 connect(d->mChangeRecorder, &Monitor::itemMoved, d, &AgentBasePrivate::itemMoved); 1199 connect(d->mChangeRecorder, &Monitor::itemRemoved, d, &AgentBasePrivate::itemRemoved); 1200 connect(d->mChangeRecorder, &Monitor::itemLinked, d, &AgentBasePrivate::itemLinked); 1201 connect(d->mChangeRecorder, &Monitor::itemUnlinked, d, &AgentBasePrivate::itemUnlinked); 1202 } 1203 } 1204 1205 QString AgentBase::identifier() const 1206 { 1207 return d_ptr->mId; 1208 } 1209 1210 void AgentBase::setAgentName(const QString &name) 1211 { 1212 Q_D(AgentBase); 1213 if (name == d->mName) { 1214 return; 1215 } 1216 1217 // TODO: rename collection 1218 d->mName = name; 1219 1220 if (d->mName.isEmpty() || d->mName == d->mId) { 1221 d->mSettings->remove(QStringLiteral("Resource/Name")); 1222 d->mSettings->remove(QStringLiteral("Agent/Name")); 1223 } else { 1224 d->mSettings->setValue(QStringLiteral("Agent/Name"), d->mName); 1225 } 1226 1227 d->mSettings->sync(); 1228 1229 d->setProgramName(); 1230 1231 Q_EMIT agentNameChanged(d->mName); 1232 } 1233 1234 QString AgentBase::agentName() const 1235 { 1236 Q_D(const AgentBase); 1237 if (d->mName.isEmpty()) { 1238 return d->mId; 1239 } else { 1240 return d->mName; 1241 } 1242 } 1243 1244 void AgentBase::changeProcessed() 1245 { 1246 Q_D(AgentBase); 1247 d->changeProcessed(); 1248 } 1249 1250 ChangeRecorder *AgentBase::changeRecorder() const 1251 { 1252 return d_ptr->mChangeRecorder; 1253 } 1254 1255 KSharedConfigPtr AgentBase::config() 1256 { 1257 return KSharedConfig::openConfig(); 1258 } 1259 1260 void AgentBase::abort() 1261 { 1262 Q_EMIT abortRequested(); 1263 } 1264 1265 void AgentBase::reconfigure() 1266 { 1267 Q_EMIT reloadConfiguration(); 1268 } 1269 1270 #include "moc_agentbase.cpp" 1271 #include "moc_agentbase_p.cpp"