1/*
2 Copyright (c) 2006 Till Adam <adam@kde.org>
3 Copyright (c) 2007 Volker Krause <vkrause@kde.org>
4 Copyright (c) 2007 Bruno Virlet <bruno.virlet@gmail.com>
5 Copyright (c) 2008 Kevin Krammer <kevin.krammer@gmx.at>
6
7 This library is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Library General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or (at your
10 option) any later version.
11
12 This library is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
15 License for more details.
16
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301, USA.
21*/
22
23#include "agentbase.h"
24#include "agentbase_p.h"
25
26#include "agentmanager.h"
27#include "changerecorder.h"
28#include "controladaptor.h"
29#include "dbusconnectionpool.h"
30#include "itemfetchjob.h"
31#include "kdepimlibs-version.h"
32#include "monitor_p.h"
33#include "servermanager_p.h"
34#include "session.h"
35#include "session_p.h"
36#include "statusadaptor.h"
37
38#include <kaboutdata.h>
39#include <kcmdlineargs.h>
40#include <kdebug.h>
41#include <kglobal.h>
42#include <klocalizedstring.h>
43#include <kstandarddirs.h>
44
45#include <Solid/PowerManagement>
46
47#include <QtCore/QDir>
48#include <QtCore/QSettings>
49#include <QtCore/QTimer>
50#include <QtDBus/QtDBus>
51#include <QApplication>
52
53#include <signal.h>
54#include <stdlib.h>
55#if defined __GLIBC__
56# include <malloc.h> // for dumping memory information
57#endif
58
59//#define EXPERIMENTAL_INPROCESS_AGENTS 1
60
61using namespace Akonadi;
62
63static AgentBase *sAgentBase = 0;
64
65AgentBase::Observer::Observer()
66{
67}
68
69AgentBase::Observer::~Observer()
70{
71}
72
73void AgentBase::Observer::itemAdded(const Item &item, const Collection &collection)
74{
75 Q_UNUSED(item);
76 Q_UNUSED(collection);
77 if (sAgentBase != 0) {
78 sAgentBase->d_ptr->changeProcessed();
79 }
80}
81
82void AgentBase::Observer::itemChanged(const Item &item, const QSet<QByteArray> &partIdentifiers)
83{
84 Q_UNUSED(item);
85 Q_UNUSED(partIdentifiers);
86 if (sAgentBase != 0) {
87 sAgentBase->d_ptr->changeProcessed();
88 }
89}
90
91void AgentBase::Observer::itemRemoved(const Item &item)
92{
93 Q_UNUSED(item);
94 if (sAgentBase != 0) {
95 sAgentBase->d_ptr->changeProcessed();
96 }
97}
98
99void AgentBase::Observer::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent)
100{
101 Q_UNUSED(collection);
102 Q_UNUSED(parent);
103 if (sAgentBase != 0) {
104 sAgentBase->d_ptr->changeProcessed();
105 }
106}
107
108void AgentBase::Observer::collectionChanged(const Collection &collection)
109{
110 Q_UNUSED(collection);
111 if (sAgentBase != 0) {
112 sAgentBase->d_ptr->changeProcessed();
113 }
114}
115
116void AgentBase::Observer::collectionRemoved(const Collection &collection)
117{
118 Q_UNUSED(collection);
119 if (sAgentBase != 0) {
120 sAgentBase->d_ptr->changeProcessed();
121 }
122}
123
124void AgentBase::ObserverV2::itemMoved(const Akonadi::Item &item, const Akonadi::Collection &source, const Akonadi::Collection &dest)
125{
126 Q_UNUSED(item);
127 Q_UNUSED(source);
128 Q_UNUSED(dest);
129 if (sAgentBase != 0) {
130 sAgentBase->d_ptr->changeProcessed();
131 }
132}
133
134void AgentBase::ObserverV2::itemLinked(const Akonadi::Item &item, const Akonadi::Collection &collection)
135{
136 Q_UNUSED(item);
137 Q_UNUSED(collection);
138 if (sAgentBase != 0) {
139 // not implementation, let's disconnect the signal to enable optimizations in Monitor
140 QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(itemLinked(Akonadi::Item,Akonadi::Collection)),
141 sAgentBase->d_ptr, SLOT(itemLinked(Akonadi::Item,Akonadi::Collection)));
142 sAgentBase->d_ptr->changeProcessed();
143 }
144}
145
146void AgentBase::ObserverV2::itemUnlinked(const Akonadi::Item &item, const Akonadi::Collection &collection)
147{
148 Q_UNUSED(item);
149 Q_UNUSED(collection);
150 if (sAgentBase != 0) {
151 // not implementation, let's disconnect the signal to enable optimizations in Monitor
152 QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(itemUnlinked(Akonadi::Item,Akonadi::Collection)),
153 sAgentBase->d_ptr, SLOT(itemUnlinked(Akonadi::Item,Akonadi::Collection)));
154 sAgentBase->d_ptr->changeProcessed();
155 }
156}
157
158void AgentBase::ObserverV2::collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &source, const Akonadi::Collection &dest)
159{
160 Q_UNUSED(collection);
161 Q_UNUSED(source);
162 Q_UNUSED(dest);
163 if (sAgentBase != 0) {
164 sAgentBase->d_ptr->changeProcessed();
165 }
166}
167
168void AgentBase::ObserverV2::collectionChanged(const Akonadi::Collection &collection, const QSet<QByteArray> &changedAttributes)
169{
170 Q_UNUSED(changedAttributes);
171 collectionChanged(collection);
172}
173
174void AgentBase::ObserverV3::itemsFlagsChanged(const Akonadi::Item::List &items, const QSet< QByteArray > &addedFlags, const QSet< QByteArray > &removedFlags)
175{
176 Q_UNUSED(items);
177 Q_UNUSED(addedFlags);
178 Q_UNUSED(removedFlags);
179
180 if (sAgentBase != 0) {
181 // not implementation, let's disconnect the signal to enable optimizations in Monitor
182 QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(itemsFlagsChanged(Akonadi::Item::List,QSet<QByteArray>,QSet<QByteArray>)),
183 sAgentBase->d_ptr, SLOT(itemsFlagsChanged(Akonadi::Item::List,QSet<QByteArray>,QSet<QByteArray>)));
184 sAgentBase->d_ptr->changeProcessed();
185 }
186}
187
188void AgentBase::ObserverV3::itemsMoved(const Akonadi::Item::List &items, const Collection &sourceCollection, const Collection &destinationCollection)
189{
190 Q_UNUSED(items);
191 Q_UNUSED(sourceCollection);
192 Q_UNUSED(destinationCollection);
193
194 if (sAgentBase != 0) {
195 // not implementation, let's disconnect the signal to enable optimizations in Monitor
196 QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(itemsMoved(Akonadi::Item::List,Akonadi::Collection,Akonadi::Collection)),
197 sAgentBase->d_ptr, SLOT(itemsMoved(Akonadi::Item::List,Akonadi::Collection,Akonadi::Collection)));
198 sAgentBase->d_ptr->changeProcessed();
199 }
200}
201
202void AgentBase::ObserverV3::itemsRemoved(const Akonadi::Item::List &items)
203{
204 Q_UNUSED(items);
205
206 if (sAgentBase != 0) {
207 // not implementation, let's disconnect the signal to enable optimizations in Monitor
208 QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(itemsRemoved(Akonadi::Item::List)),
209 sAgentBase->d_ptr, SLOT(itemsRemoved(Akonadi::Item::List)));
210 sAgentBase->d_ptr->changeProcessed();
211 }
212}
213
214void AgentBase::ObserverV3::itemsLinked(const Akonadi::Item::List &items, const Collection &collection)
215{
216 Q_UNUSED(items);
217 Q_UNUSED(collection);
218
219 if (sAgentBase != 0) {
220 // not implementation, let's disconnect the signal to enable optimizations in Monitor
221 QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(itemsLinked(Akonadi::Item::List,Akonadi::Collection)),
222 sAgentBase->d_ptr, SLOT(itemsLinked(Akonadi::Item::List,Akonadi::Collection)));
223 sAgentBase->d_ptr->changeProcessed();
224 }
225}
226
227void AgentBase::ObserverV3::itemsUnlinked(const Akonadi::Item::List &items, const Collection &collection)
228{
229 Q_UNUSED(items);
230 Q_UNUSED(collection)
231
232 if (sAgentBase != 0) {
233 // not implementation, let's disconnect the signal to enable optimizations in Monitor
234 QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(itemsUnlinked(Akonadi::Item::List,Akonadi::Collection)),
235 sAgentBase->d_ptr, SLOT(itemsUnlinked(Akonadi::Item::List,Akonadi::Collection)));
236 sAgentBase->d_ptr->changeProcessed();
237 }
238}
239
240void AgentBase::ObserverV4::tagAdded(const Tag &tag)
241{
242 Q_UNUSED(tag);
243
244 if (sAgentBase != 0) {
245 // not implementation, let's disconnect the signal to enable optimization in Monitor
246 QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(tagAdded(Akonadi::Tag)),
247 sAgentBase->d_ptr, SLOT(tagAdded(Akonadi::Tag)));
248 sAgentBase->d_ptr->changeProcessed();
249 }
250}
251
252void AgentBase::ObserverV4::tagChanged(const Tag &tag)
253{
254 Q_UNUSED(tag);
255
256 if (sAgentBase != 0) {
257 // not implementation, let's disconnect the signal to enable optimization in Monitor
258 QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(tagChanged(Akonadi::Tag)),
259 sAgentBase->d_ptr, SLOT(tagChanged(Akonadi::Tag)));
260 sAgentBase->d_ptr->changeProcessed();
261 }
262}
263
264void AgentBase::ObserverV4::tagRemoved(const Tag &tag)
265{
266 Q_UNUSED(tag);
267
268 if (sAgentBase != 0) {
269 // not implementation, let's disconnect the signal to enable optimization in Monitor
270 QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(tagRemoved(Akonadi::Tag)),
271 sAgentBase->d_ptr, SLOT(tagRemoved(Akonadi::Tag)));
272 sAgentBase->d_ptr->changeProcessed();
273 }
274}
275
276void AgentBase::ObserverV4::itemsTagsChanged(const Item::List &items, const QSet<Tag> &addedTags, const QSet<Tag> &removedTags)
277{
278 Q_UNUSED(items);
279 Q_UNUSED(addedTags);
280 Q_UNUSED(removedTags);
281
282 if (sAgentBase != 0) {
283 // not implementation, let's disconnect the signal to enable optimization in Monitor
284 QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(itemsTagsChanged(Akonadi::Item::List,QSet<Akonadi::Tag>,QSet<Akonadi::Tag>)),
285 sAgentBase->d_ptr, SLOT(itemsTagsChanged(Akonadi::Item::List,QSet<Akonadi::Tag>,QSet<Akonadi::Tag>)));
286 sAgentBase->d_ptr->changeProcessed();
287 }
288}
289
290//@cond PRIVATE
291
292AgentBasePrivate::AgentBasePrivate(AgentBase *parent)
293 : q_ptr(parent)
294 , mDBusConnection(QString())
295 , mStatusCode(AgentBase::Idle)
296 , mProgress(0)
297 , mNeedsNetwork(false)
298 , mOnline(false)
299 , mDesiredOnlineState(false)
300 , mSettings(0)
301 , mChangeRecorder(0)
302 , mTracer(0)
303 , mObserver(0)
304 , mTemporaryOfflineTimer(0)
305{
306 Internal::setClientType(Internal::Agent);
307}
308
309AgentBasePrivate::~AgentBasePrivate()
310{
311 mChangeRecorder->setConfig(0);
312 delete mSettings;
313}
314
315void AgentBasePrivate::init()
316{
317 Q_Q(AgentBase);
318
319 /**
320 * Create a default session for this process.
321 */
322 SessionPrivate::createDefaultSession(mId.toLatin1());
323
324 if (QThread::currentThread() != QCoreApplication::instance()->thread()) {
325 mDBusConnection = QDBusConnection::connectToBus(QDBusConnection::SessionBus, q->identifier());
326 Q_ASSERT(mDBusConnection.isConnected());
327 }
328
329 mTracer = new org::freedesktop::Akonadi::Tracer(ServerManager::serviceName(ServerManager::Server),
330 QLatin1String("/tracing"),
331 DBusConnectionPool::threadConnection(), q);
332
333 new Akonadi__ControlAdaptor(q);
334 new Akonadi__StatusAdaptor(q);
335 if (!DBusConnectionPool::threadConnection().registerObject(QLatin1String("/"), q, QDBusConnection::ExportAdaptors)) {
336 q->error(i18n("Unable to register object at dbus: %1", DBusConnectionPool::threadConnection().lastError().message()));
337 }
338
339 mSettings = new QSettings(QString::fromLatin1("%1/agent_config_%2").arg(Internal::xdgSaveDir("config"), mId), QSettings::IniFormat);
340
341 mChangeRecorder = new ChangeRecorder(q);
342 mChangeRecorder->ignoreSession(Session::defaultSession());
343 mChangeRecorder->itemFetchScope().setCacheOnly(true);
344 mChangeRecorder->setConfig(mSettings);
345
346 mDesiredOnlineState = mSettings->value(QLatin1String("Agent/DesiredOnlineState"), true).toBool();
347 mOnline = mDesiredOnlineState;
348
349 // reinitialize the status message now that online state is available
350 mStatusMessage = defaultReadyMessage();
351
352 mName = mSettings->value(QLatin1String("Agent/Name")).toString();
353 if (mName.isEmpty()) {
354 mName = mSettings->value(QLatin1String("Resource/Name")).toString();
355 if (!mName.isEmpty()) {
356 mSettings->remove(QLatin1String("Resource/Name"));
357 mSettings->setValue(QLatin1String("Agent/Name"), mName);
358 }
359 }
360
361 connect(mChangeRecorder, SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection)),
362 SLOT(itemAdded(Akonadi::Item,Akonadi::Collection)));
363 connect(mChangeRecorder, SIGNAL(itemChanged(Akonadi::Item,QSet<QByteArray>)),
364 SLOT(itemChanged(Akonadi::Item,QSet<QByteArray>)));
365 connect(mChangeRecorder, SIGNAL(collectionAdded(Akonadi::Collection,Akonadi::Collection)),
366 SLOT(collectionAdded(Akonadi::Collection,Akonadi::Collection)));
367 connect(mChangeRecorder, SIGNAL(collectionChanged(Akonadi::Collection)),
368 SLOT(collectionChanged(Akonadi::Collection)));
369 connect(mChangeRecorder, SIGNAL(collectionChanged(Akonadi::Collection,QSet<QByteArray>)),
370 SLOT(collectionChanged(Akonadi::Collection,QSet<QByteArray>)));
371 connect(mChangeRecorder, SIGNAL(collectionMoved(Akonadi::Collection,Akonadi::Collection,Akonadi::Collection)),
372 SLOT(collectionMoved(Akonadi::Collection,Akonadi::Collection,Akonadi::Collection)));
373 connect(mChangeRecorder, SIGNAL(collectionRemoved(Akonadi::Collection)),
374 SLOT(collectionRemoved(Akonadi::Collection)));
375 connect(mChangeRecorder, SIGNAL(collectionSubscribed(Akonadi::Collection,Akonadi::Collection)),
376 SLOT(collectionSubscribed(Akonadi::Collection,Akonadi::Collection)));
377 connect(mChangeRecorder, SIGNAL(collectionUnsubscribed(Akonadi::Collection)),
378 SLOT(collectionUnsubscribed(Akonadi::Collection)));
379
380 connect(q, SIGNAL(status(int,QString)), q, SLOT(slotStatus(int,QString)));
381 connect(q, SIGNAL(percent(int)), q, SLOT(slotPercent(int)));
382 connect(q, SIGNAL(warning(QString)), q, SLOT(slotWarning(QString)));
383 connect(q, SIGNAL(error(QString)), q, SLOT(slotError(QString)));
384
385 connect(Solid::PowerManagement::notifier(), SIGNAL(resumingFromSuspend()), q, SLOT(slotResumedFromSuspend()));
386
387 // Use reference counting to allow agents to finish internal jobs when the
388 // agent is stopped.
389 KGlobal::ref();
390 if (QThread::currentThread() == QCoreApplication::instance()->thread()) {
391 KGlobal::setAllowQuit(true);
392 }
393
394 // disable session management
395 if (KApplication::kApplication()) {
396 KApplication::kApplication()->disableSessionManagement();
397 }
398
399 mResourceTypeName = AgentManager::self()->instance(mId).type().name();
400 setProgramName();
401
402 QTimer::singleShot(0, q, SLOT(delayedInit()));
403}
404
405void AgentBasePrivate::delayedInit()
406{
407 Q_Q(AgentBase);
408
409 const QString serviceId = ServerManager::agentServiceName(ServerManager::Agent, mId);
410 if (!DBusConnectionPool::threadConnection().registerService(serviceId)) {
411 kFatal() << "Unable to register service" << serviceId << "at dbus:" << DBusConnectionPool::threadConnection().lastError().message();
412 }
413 q->setOnlineInternal(mDesiredOnlineState);
414
415 DBusConnectionPool::threadConnection().registerObject(QLatin1String("/Debug"), this, QDBusConnection::ExportScriptableSlots);
416}
417
418void AgentBasePrivate::setProgramName()
419{
420 // ugly, really ugly, if you find another solution, change it and blame me for this code (Andras)
421 QString programName = mResourceTypeName;
422 if (!mName.isEmpty()) {
423 programName = i18nc("Name and type of Akonadi resource", "%1 of type %2", mName, mResourceTypeName) ;
424 }
425 const_cast<KAboutData *>(KGlobal::mainComponent().aboutData())->setProgramName(ki18n(programName.toUtf8()));
426}
427
428void AgentBasePrivate::itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection)
429{
430 if (mObserver != 0) {
431 mObserver->itemAdded(item, collection);
432 }
433}
434
435void AgentBasePrivate::itemChanged(const Akonadi::Item &item, const QSet<QByteArray> &partIdentifiers)
436{
437 if (mObserver != 0) {
438 mObserver->itemChanged(item, partIdentifiers);
439 }
440}
441
442void AgentBasePrivate::itemMoved(const Akonadi::Item &item, const Akonadi::Collection &source, const Akonadi::Collection &dest)
443{
444 AgentBase::ObserverV2 *observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
445 if (mObserver) {
446 // inter-resource moves, requires we know which resources the source and destination are in though
447 if (!source.resource().isEmpty() && !dest.resource().isEmpty()) {
448 if (source.resource() != dest.resource()) {
449 if (source.resource() == q_ptr->identifier()) { // moved away from us
450 Akonadi::Item i(item);
451 i.setParentCollection(source);
452 mObserver->itemRemoved(i);
453 } else if (dest.resource() == q_ptr->identifier()) { // moved to us
454 mObserver->itemAdded(item, dest);
455 } else if (observer2) {
456 observer2->itemMoved(item, source, dest);
457 } else {
458 // not for us, not sure if we should get here at all
459 changeProcessed();
460 }
461 return;
462 }
463 }
464 // intra-resource move
465 if (observer2) {
466 observer2->itemMoved(item, source, dest);
467 } else {
468 // ### we cannot just call itemRemoved here as this will already trigger changeProcessed()
469 // so, just itemAdded() is good enough as no resource can have implemented intra-resource moves anyway
470 // without using ObserverV2
471 mObserver->itemAdded(item, dest);
472 // mObserver->itemRemoved( item );
473 }
474 }
475}
476
477void AgentBasePrivate::itemRemoved(const Akonadi::Item &item)
478{
479 if (mObserver != 0) {
480 mObserver->itemRemoved(item);
481 }
482}
483
484void AgentBasePrivate::itemLinked(const Akonadi::Item &item, const Akonadi::Collection &collection)
485{
486 AgentBase::ObserverV2 *observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
487 if (observer2) {
488 observer2->itemLinked(item, collection);
489 } else {
490 changeProcessed();
491 }
492}
493
494void AgentBasePrivate::itemUnlinked(const Akonadi::Item &item, const Akonadi::Collection &collection)
495{
496 AgentBase::ObserverV2 *observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
497 if (observer2) {
498 observer2->itemUnlinked(item, collection);
499 } else {
500 changeProcessed();
501 }
502}
503
504void AgentBasePrivate::itemsFlagsChanged(const Akonadi::Item::List &items, const QSet<QByteArray> &addedFlags, const QSet<QByteArray> &removedFlags)
505{
506 AgentBase::ObserverV3 *observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver);
507 if (observer3) {
508 observer3->itemsFlagsChanged(items, addedFlags, removedFlags);
509 } else {
510 Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available");
511 }
512}
513
514void AgentBasePrivate::itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &source, const Akonadi::Collection &destination)
515{
516 AgentBase::ObserverV3 *observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver);
517 if (observer3) {
518 observer3->itemsMoved(items, source, destination);
519 } else {
520 Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available");
521 }
522}
523
524void AgentBasePrivate::itemsRemoved(const Akonadi::Item::List &items)
525{
526 AgentBase::ObserverV3 *observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver);
527 if (observer3) {
528 observer3->itemsRemoved(items);
529 } else {
530 Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available");
531 }
532}
533
534void AgentBasePrivate::itemsLinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection)
535{
536 if (!mObserver) {
537 changeProcessed();
538 return;
539 }
540
541 AgentBase::ObserverV3 *observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver);
542 if (observer3) {
543 observer3->itemsLinked(items, collection);
544 } else {
545 Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available");
546 }
547}
548
549void AgentBasePrivate::itemsUnlinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection)
550{
551 if (!mObserver) {
552 return;
553 }
554
555 AgentBase::ObserverV3 *observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver);
556 if (observer3) {
557 observer3->itemsUnlinked(items, collection);
558 } else {
559 Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available");
560 }
561}
562
563void AgentBasePrivate::tagAdded(const Akonadi::Tag &tag)
564{
565 if (!mObserver) {
566 return;
567 }
568
569 AgentBase::ObserverV4 *observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
570 if (observer4) {
571 observer4->tagAdded(tag);
572 } else {
573 changeProcessed();
574 }
575}
576
577void AgentBasePrivate::tagChanged(const Akonadi::Tag &tag)
578{
579 if (!mObserver) {
580 return;
581 }
582
583 AgentBase::ObserverV4 *observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
584 if (observer4) {
585 observer4->tagChanged(tag);
586 } else {
587 changeProcessed();
588 }
589}
590
591void AgentBasePrivate::tagRemoved(const Akonadi::Tag &tag)
592{
593 if (!mObserver) {
594 return;
595 }
596
597 AgentBase::ObserverV4 *observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
598 if (observer4) {
599 observer4->tagRemoved(tag);;
600 } else {
601 changeProcessed();
602 }
603}
604
605void AgentBasePrivate::itemsTagsChanged(const Akonadi::Item::List &items, const QSet<Akonadi::Tag> &addedTags, const QSet<Akonadi::Tag> &removedTags)
606{
607 if (!mObserver) {
608 return;
609 }
610
611 AgentBase::ObserverV4 *observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
612 if (observer4) {
613 observer4->itemsTagsChanged(items, addedTags, removedTags);
614 } else {
615 changeProcessed();
616 }
617}
618
619void AgentBasePrivate::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent)
620{
621 if (mObserver != 0) {
622 mObserver->collectionAdded(collection, parent);
623 }
624}
625
626void AgentBasePrivate::collectionChanged(const Akonadi::Collection &collection)
627{
628 AgentBase::ObserverV2 *observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
629 if (mObserver != 0 && observer2 == 0) { // For ObserverV2 we use the variant with the part identifiers
630 mObserver->collectionChanged(collection);
631 }
632}
633
634void AgentBasePrivate::collectionChanged(const Akonadi::Collection &collection, const QSet<QByteArray> &changedAttributes)
635{
636 AgentBase::ObserverV2 *observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
637 if (observer2 != 0) {
638 observer2->collectionChanged(collection, changedAttributes);
639 }
640}
641
642void AgentBasePrivate::collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &source, const Akonadi::Collection &dest)
643{
644 AgentBase::ObserverV2 *observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
645 if (observer2) {
646 observer2->collectionMoved(collection, source, dest);
647 } else if (mObserver) {
648 // ### we cannot just call collectionRemoved here as this will already trigger changeProcessed()
649 // so, just collectionAdded() is good enough as no resource can have implemented intra-resource moves anyway
650 // without using ObserverV2
651 mObserver->collectionAdded(collection, dest);
652 } else {
653 changeProcessed();
654 }
655}
656
657void AgentBasePrivate::collectionRemoved(const Akonadi::Collection &collection)
658{
659 if (mObserver != 0) {
660 mObserver->collectionRemoved(collection);
661 }
662}
663
664void AgentBasePrivate::collectionSubscribed(const Akonadi::Collection &collection, const Akonadi::Collection &parent)
665{
666 Q_UNUSED(collection);
667 Q_UNUSED(parent);
668 changeProcessed();
669}
670
671void AgentBasePrivate::collectionUnsubscribed(const Akonadi::Collection &collection)
672{
673 Q_UNUSED(collection);
674 changeProcessed();
675}
676
677void AgentBasePrivate::changeProcessed()
678{
679 mChangeRecorder->changeProcessed();
680 QTimer::singleShot(0, mChangeRecorder, SLOT(replayNext()));
681}
682
683void AgentBasePrivate::slotStatus(int status, const QString &message)
684{
685 mStatusMessage = message;
686 mStatusCode = 0;
687
688 switch (status) {
689 case AgentBase::Idle:
690 if (mStatusMessage.isEmpty()) {
691 mStatusMessage = defaultReadyMessage();
692 }
693
694 mStatusCode = 0;
695 break;
696 case AgentBase::Running:
697 if (mStatusMessage.isEmpty()) {
698 mStatusMessage = defaultSyncingMessage();
699 }
700
701 mStatusCode = 1;
702 break;
703 case AgentBase::Broken:
704 if (mStatusMessage.isEmpty()) {
705 mStatusMessage = defaultErrorMessage();
706 }
707
708 mStatusCode = 2;
709 break;
710
711 case AgentBase::NotConfigured:
712 if (mStatusMessage.isEmpty()) {
713 mStatusMessage = defaultUnconfiguredMessage();
714 }
715
716 mStatusCode = 3;
717 break;
718
719 default:
720 Q_ASSERT(!"Unknown status passed");
721 break;
722 }
723}
724
725void AgentBasePrivate::slotPercent(int progress)
726{
727 mProgress = progress;
728}
729
730void AgentBasePrivate::slotWarning(const QString &message)
731{
732 mTracer->warning(QString::fromLatin1("AgentBase(%1)").arg(mId), message);
733}
734
735void AgentBasePrivate::slotError(const QString &message)
736{
737 mTracer->error(QString::fromLatin1("AgentBase(%1)").arg(mId), message);
738}
739
740void AgentBasePrivate::slotNetworkStatusChange(Solid::Networking::Status stat)
741{
742 Q_UNUSED(stat);
743 Q_Q(AgentBase);
744 q->setOnlineInternal(mDesiredOnlineState);
745}
746
747void AgentBasePrivate::slotResumedFromSuspend()
748{
749 if (mNeedsNetwork) {
750 slotNetworkStatusChange(Solid::Networking::status());
751 }
752}
753
754void AgentBasePrivate::slotTemporaryOfflineTimeout()
755{
756 Q_Q(AgentBase);
757 q->setOnlineInternal(true);
758}
759
760QString AgentBasePrivate::dumpNotificationListToString() const
761{
762 return mChangeRecorder->dumpNotificationListToString();
763}
764
765void AgentBasePrivate::dumpMemoryInfo() const
766{
767 // Send it to stdout, so we can debug user problems.
768 // since you have to explicitely call this
769 // it won't flood users with release builds.
770 QTextStream stream(stdout);
771 stream << dumpMemoryInfoToString();
772}
773
774QString AgentBasePrivate::dumpMemoryInfoToString() const
775{
776 // man mallinfo for more info
777 QString str;
778#if defined __GLIBC__
779 struct mallinfo mi;
780 mi = mallinfo();
781 QTextStream stream(&str);
782 stream
783 << "Total non-mmapped bytes (arena): " << mi.arena << '\n'
784 << "# of free chunks (ordblks): " << mi.ordblks << '\n'
785 << "# of free fastbin blocks (smblks>: " << mi.smblks << '\n'
786 << "# of mapped regions (hblks): " << mi.hblks << '\n'
787 << "Bytes in mapped regions (hblkhd): " << mi.hblkhd << '\n'
788 << "Max. total allocated space (usmblks): " << mi.usmblks << '\n'
789 << "Free bytes held in fastbins (fsmblks):" << mi.fsmblks << '\n'
790 << "Total allocated space (uordblks): " << mi.uordblks << '\n'
791 << "Total free space (fordblks): " << mi.fordblks << '\n'
792 << "Topmost releasable block (keepcost): " << mi.keepcost << '\n';
793#else
794 str = QLatin1String( "mallinfo() not supported" );
795#endif
796 return str;
797}
798
799AgentBase::AgentBase(const QString &id)
800 : d_ptr(new AgentBasePrivate(this))
801{
802 sAgentBase = this;
803 d_ptr->mId = id;
804 d_ptr->init();
805}
806
807AgentBase::AgentBase(AgentBasePrivate *d, const QString &id)
808 : d_ptr(d)
809{
810 sAgentBase = this;
811 d_ptr->mId = id;
812 d_ptr->init();
813}
814
815AgentBase::~AgentBase()
816{
817 delete d_ptr;
818}
819
820QString AgentBase::parseArguments(int argc, char **argv)
821{
822 QString identifier;
823 if (argc < 3) {
824 kDebug() << "Not enough arguments passed...";
825 exit(1);
826 }
827
828 for (int i = 1; i < argc - 1; ++i) {
829 if (QLatin1String(argv[i]) == QLatin1String("--identifier")) {
830 identifier = QLatin1String(argv[i + 1]);
831 }
832 }
833
834 if (identifier.isEmpty()) {
835 kDebug() << "Identifier argument missing";
836 exit(1);
837 }
838
839 const QFileInfo fi(QString::fromLocal8Bit(argv[0]));
840 // strip off full path and possible .exe suffix
841 const QByteArray catalog = fi.baseName().toLatin1();
842
843 KCmdLineArgs::init(argc, argv, ServerManager::addNamespace(identifier).toLatin1(), catalog, ki18n("Akonadi Agent"), KDEPIMLIBS_VERSION,
844 ki18n("Akonadi Agent"));
845
846 KCmdLineOptions options;
847 options.add("identifier <argument>", ki18n("Agent identifier"));
848 KCmdLineArgs::addCmdLineOptions(options);
849
850 return identifier;
851}
852
853// @endcond
854
855int AgentBase::init(AgentBase *r)
856{
857 QApplication::setQuitOnLastWindowClosed(false);
858 KGlobal::locale()->insertCatalog(QLatin1String("libakonadi"));
859 int rv = kapp->exec();
860 delete r;
861 return rv;
862}
863
864int AgentBase::status() const
865{
866 Q_D(const AgentBase);
867
868 return d->mStatusCode;
869}
870
871QString AgentBase::statusMessage() const
872{
873 Q_D(const AgentBase);
874
875 return d->mStatusMessage;
876}
877
878int AgentBase::progress() const
879{
880 Q_D(const AgentBase);
881
882 return d->mProgress;
883}
884
885QString AgentBase::progressMessage() const
886{
887 Q_D(const AgentBase);
888
889 return d->mProgressMessage;
890}
891
892bool AgentBase::isOnline() const
893{
894 Q_D(const AgentBase);
895
896 return d->mOnline;
897}
898
899void AgentBase::setNeedsNetwork(bool needsNetwork)
900{
901 Q_D(AgentBase);
902 d->mNeedsNetwork = needsNetwork;
903
904 if (d->mNeedsNetwork) {
905 connect(Solid::Networking::notifier()
906 , SIGNAL(statusChanged(Solid::Networking::Status))
907 , this, SLOT(slotNetworkStatusChange(Solid::Networking::Status))
908 , Qt::UniqueConnection);
909
910 } else {
911 disconnect(Solid::Networking::notifier(), 0, 0, 0);
912 setOnlineInternal(d->mDesiredOnlineState);
913 }
914}
915
916void AgentBase::setOnline(bool state)
917{
918 Q_D(AgentBase);
919 d->mDesiredOnlineState = state;
920 d->mSettings->setValue(QLatin1String("Agent/DesiredOnlineState"), state);
921 setOnlineInternal(state);
922}
923
924void AgentBase::setTemporaryOffline(int makeOnlineInSeconds)
925{
926 Q_D(AgentBase);
927
928 // if not currently online, avoid bringing it online after the timeout
929 if (!d->mOnline) {
930 return;
931 }
932
933 setOnlineInternal(false);
934
935 if (!d->mTemporaryOfflineTimer) {
936 d->mTemporaryOfflineTimer = new QTimer(d);
937 d->mTemporaryOfflineTimer->setSingleShot(true);
938 connect(d->mTemporaryOfflineTimer, SIGNAL(timeout()), this, SLOT(slotTemporaryOfflineTimeout()));
939 }
940 d->mTemporaryOfflineTimer->setInterval(makeOnlineInSeconds * 1000);
941 d->mTemporaryOfflineTimer->start();
942}
943
944void AgentBase::setOnlineInternal( bool state )
945{
946 Q_D(AgentBase);
947 if (state && d->mNeedsNetwork) {
948 const Solid::Networking::Status stat = Solid::Networking::status();
949 if (stat != Solid::Networking::Unknown && stat != Solid::Networking::Connected) {
950 //Don't go online if the resource needs network but there is none
951 state = false;
952 }
953 }
954 d->mOnline = state;
955
956 if (d->mTemporaryOfflineTimer) {
957 d->mTemporaryOfflineTimer->stop();
958 }
959
960 const QString newMessage = d->defaultReadyMessage();
961 if ( d->mStatusMessage != newMessage && d->mStatusCode != AgentBase::Broken ) {
962 emit status( d->mStatusCode, newMessage );
963 }
964
965 doSetOnline(state);
966 emit onlineChanged(state);
967}
968
969void AgentBase::doSetOnline(bool online)
970{
971 Q_UNUSED(online);
972}
973
974void AgentBase::configure(WId windowId)
975{
976 Q_UNUSED(windowId);
977 emit configurationDialogAccepted();
978}
979
980#ifdef Q_OS_WIN //krazy:exclude=cpp
981void AgentBase::configure(qlonglong windowId)
982{
983 configure(reinterpret_cast<WId>(windowId));
984}
985#endif
986
987WId AgentBase::winIdForDialogs() const
988{
989 const bool registered = DBusConnectionPool::threadConnection().interface()->isServiceRegistered(QLatin1String("org.freedesktop.akonaditray"));
990 if (!registered) {
991 return 0;
992 }
993
994 QDBusInterface dbus(QLatin1String("org.freedesktop.akonaditray"), QLatin1String("/Actions"),
995 QLatin1String("org.freedesktop.Akonadi.Tray"));
996 const QDBusMessage reply = dbus.call(QLatin1String("getWinId"));
997
998 if (reply.type() == QDBusMessage::ErrorMessage) {
999 return 0;
1000 }
1001
1002 const WId winid = (WId)reply.arguments().at(0).toLongLong();
1003
1004 return winid;
1005}
1006
1007void AgentBase::quit()
1008{
1009 Q_D(AgentBase);
1010 aboutToQuit();
1011
1012 if (d->mSettings) {
1013 d->mChangeRecorder->setConfig(0);
1014 d->mSettings->sync();
1015 }
1016
1017 KGlobal::deref();
1018}
1019
1020void AgentBase::aboutToQuit()
1021{
1022}
1023
1024void AgentBase::cleanup()
1025{
1026 Q_D(AgentBase);
1027 // prevent the monitor from picking up deletion signals for our own data if we are a resource
1028 // and thus avoid that we kill our own data as last act before our own death
1029 d->mChangeRecorder->blockSignals(true);
1030
1031 aboutToQuit();
1032
1033 const QString fileName = d->mSettings->fileName();
1034
1035 /*
1036 * First destroy the settings object...
1037 */
1038 d->mChangeRecorder->setConfig(0);
1039 delete d->mSettings;
1040 d->mSettings = 0;
1041
1042 /*
1043 * ... then remove the file from hd.
1044 */
1045 QFile::remove(fileName);
1046
1047 /*
1048 * ... and remove the changes file from hd.
1049 */
1050 QFile::remove(fileName + QLatin1String("_changes.dat"));
1051
1052 /*
1053 * ... and also remove the agent configuration file if there is one.
1054 */
1055 QString configFile = KStandardDirs::locateLocal("config", config()->name());
1056 QFile::remove(configFile);
1057
1058 KGlobal::deref();
1059}
1060
1061void AgentBase::registerObserver(Observer *observer)
1062{
1063 // TODO in theory we should re-connect change recorder signals here that we disconnected previously
1064 d_ptr->mObserver = observer;
1065
1066 const bool hasObserverV3 = (dynamic_cast<AgentBase::ObserverV3 *>(d_ptr->mObserver) != 0);
1067 const bool hasObserverV4 = (dynamic_cast<AgentBase::ObserverV4 *>(d_ptr->mObserver) != 0);
1068
1069 disconnect(d_ptr->mChangeRecorder, SIGNAL(tagAdded(Akonadi::Tag)),
1070 d_ptr, SLOT(tagAdded(Akonadi::Tag)));
1071 disconnect(d_ptr->mChangeRecorder, SIGNAL(tagChanged(Akonadi::Tag)),
1072 d_ptr, SLOT(tagChanged(Akonadi::Tag)));
1073 disconnect(d_ptr->mChangeRecorder, SIGNAL(tagRemoved(Akonadi::Tag)),
1074 d_ptr, SLOT(tagRemoved(Akonadi::Tag)));
1075 disconnect(d_ptr->mChangeRecorder, SIGNAL(itemsTagsChanged(Akonadi::Item::List,QSet<Akonadi::Tag>,QSet<Akonadi::Tag>)),
1076 d_ptr, SLOT(itemsTagsChanged(Akonadi::Item::List,QSet<Akonadi::Tag>,QSet<Akonadi::Tag>)));
1077 disconnect(d_ptr->mChangeRecorder, SIGNAL(itemsFlagsChanged(Akonadi::Item::List,QSet<QByteArray>,QSet<QByteArray>)),
1078 d_ptr, SLOT(itemsFlagsChanged(Akonadi::Item::List,QSet<QByteArray>,QSet<QByteArray>)));
1079 disconnect(d_ptr->mChangeRecorder, SIGNAL(itemsMoved(Akonadi::Item::List,Akonadi::Collection,Akonadi::Collection)),
1080 d_ptr, SLOT(itemsMoved(Akonadi::Item::List,Akonadi::Collection,Akonadi::Collection)));
1081 disconnect(d_ptr->mChangeRecorder, SIGNAL(itemsRemoved(Akonadi::Item::List)),
1082 d_ptr, SLOT(itemsRemoved(Akonadi::Item::List)));
1083 disconnect(d_ptr->mChangeRecorder, SIGNAL(itemsLinked(Akonadi::Item::List,Akonadi::Collection)),
1084 d_ptr, SLOT(itemsLinked(Akonadi::Item::List,Akonadi::Collection)));
1085 disconnect(d_ptr->mChangeRecorder, SIGNAL(itemsUnlinked(Akonadi::Item::List,Akonadi::Collection)),
1086 d_ptr, SLOT(itemsUnlinked(Akonadi::Item::List,Akonadi::Collection)));
1087 disconnect(d_ptr->mChangeRecorder, SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)),
1088 d_ptr, SLOT(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)));
1089 disconnect(d_ptr->mChangeRecorder, SIGNAL(itemRemoved(Akonadi::Item)),
1090 d_ptr, SLOT(itemRemoved(Akonadi::Item)));
1091 disconnect(d_ptr->mChangeRecorder, SIGNAL(itemLinked(Akonadi::Item,Akonadi::Collection)),
1092 d_ptr, SLOT(itemLinked(Akonadi::Item,Akonadi::Collection)));
1093 disconnect(d_ptr->mChangeRecorder, SIGNAL(itemUnlinked(Akonadi::Item,Akonadi::Collection)),
1094 d_ptr, SLOT(itemUnlinked(Akonadi::Item,Akonadi::Collection)));
1095
1096 if (hasObserverV4) {
1097 connect(d_ptr->mChangeRecorder, SIGNAL(tagAdded(Akonadi::Tag)),
1098 d_ptr, SLOT(tagAdded(Akonadi::Tag)));
1099 connect(d_ptr->mChangeRecorder, SIGNAL(tagChanged(Akonadi::Tag)),
1100 d_ptr, SLOT(tagChanged(Akonadi::Tag)));
1101 connect(d_ptr->mChangeRecorder, SIGNAL(tagRemoved(Akonadi::Tag)),
1102 d_ptr, SLOT(tagRemoved(Akonadi::Tag)));
1103 connect(d_ptr->mChangeRecorder, SIGNAL(itemsTagsChanged(Akonadi::Item::List,QSet<Akonadi::Tag>,QSet<Akonadi::Tag>)),
1104 d_ptr, SLOT(itemsTagsChanged(Akonadi::Item::List,QSet<Akonadi::Tag>,QSet<Akonadi::Tag>)));
1105 }
1106
1107 if (hasObserverV3) {
1108 connect(d_ptr->mChangeRecorder, SIGNAL(itemsFlagsChanged(Akonadi::Item::List,QSet<QByteArray>,QSet<QByteArray>)),
1109 d_ptr, SLOT(itemsFlagsChanged(Akonadi::Item::List,QSet<QByteArray>,QSet<QByteArray>)));
1110 connect(d_ptr->mChangeRecorder, SIGNAL(itemsMoved(Akonadi::Item::List,Akonadi::Collection,Akonadi::Collection)),
1111 d_ptr, SLOT(itemsMoved(Akonadi::Item::List,Akonadi::Collection,Akonadi::Collection)));
1112 connect(d_ptr->mChangeRecorder, SIGNAL(itemsRemoved(Akonadi::Item::List)),
1113 d_ptr, SLOT(itemsRemoved(Akonadi::Item::List)));
1114 connect(d_ptr->mChangeRecorder, SIGNAL(itemsLinked(Akonadi::Item::List,Akonadi::Collection)),
1115 d_ptr, SLOT(itemsLinked(Akonadi::Item::List,Akonadi::Collection)));
1116 connect(d_ptr->mChangeRecorder, SIGNAL(itemsUnlinked(Akonadi::Item::List,Akonadi::Collection)),
1117 d_ptr, SLOT(itemsUnlinked(Akonadi::Item::List,Akonadi::Collection)));
1118 } else {
1119 // V2 - don't connect these if we have V3
1120 connect(d_ptr->mChangeRecorder, SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)),
1121 d_ptr, SLOT(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)));
1122 connect(d_ptr->mChangeRecorder, SIGNAL(itemRemoved(Akonadi::Item)),
1123 d_ptr, SLOT(itemRemoved(Akonadi::Item)));
1124 connect(d_ptr->mChangeRecorder, SIGNAL(itemLinked(Akonadi::Item,Akonadi::Collection)),
1125 d_ptr, SLOT(itemLinked(Akonadi::Item,Akonadi::Collection)));
1126 connect(d_ptr->mChangeRecorder, SIGNAL(itemUnlinked(Akonadi::Item,Akonadi::Collection)),
1127 d_ptr, SLOT(itemUnlinked(Akonadi::Item,Akonadi::Collection)));
1128 }
1129}
1130
1131QString AgentBase::identifier() const
1132{
1133 return d_ptr->mId;
1134}
1135
1136void AgentBase::setAgentName(const QString &name)
1137{
1138 Q_D(AgentBase);
1139 if (name == d->mName) {
1140 return;
1141 }
1142
1143 // TODO: rename collection
1144 d->mName = name;
1145
1146 if (d->mName.isEmpty() || d->mName == d->mId) {
1147 d->mSettings->remove(QLatin1String("Resource/Name"));
1148 d->mSettings->remove(QLatin1String("Agent/Name"));
1149 } else
1150 d->mSettings->setValue(QLatin1String("Agent/Name"), d->mName);
1151
1152 d->mSettings->sync();
1153
1154 d->setProgramName();
1155
1156 emit agentNameChanged(d->mName);
1157}
1158
1159QString AgentBase::agentName() const
1160{
1161 Q_D(const AgentBase);
1162 if (d->mName.isEmpty()) {
1163 return d->mId;
1164 } else {
1165 return d->mName;
1166 }
1167}
1168
1169void AgentBase::changeProcessed()
1170{
1171 Q_D(AgentBase);
1172 d->changeProcessed();
1173}
1174
1175ChangeRecorder *AgentBase::changeRecorder() const
1176{
1177 return d_ptr->mChangeRecorder;
1178}
1179
1180KSharedConfigPtr AgentBase::config()
1181{
1182 if (QCoreApplication::instance()->thread() == QThread::currentThread()) {
1183 return KGlobal::config();
1184 } else {
1185 return componentData().config();
1186 }
1187}
1188
1189void AgentBase::abort()
1190{
1191 emit abortRequested();
1192}
1193
1194void AgentBase::reconfigure()
1195{
1196 emit reloadConfiguration();
1197}
1198
1199extern QThreadStorage<KComponentData *> s_agentComponentDatas;
1200
1201KComponentData AgentBase::componentData()
1202{
1203 if (QThread::currentThread() == QCoreApplication::instance()->thread()) {
1204 if (s_agentComponentDatas.hasLocalData()) {
1205 return *(s_agentComponentDatas.localData());
1206 } else {
1207 return KGlobal::mainComponent();
1208 }
1209 }
1210
1211 Q_ASSERT(s_agentComponentDatas.hasLocalData());
1212 return *(s_agentComponentDatas.localData());
1213}
1214
1215#include "moc_agentbase.cpp"
1216#include "moc_agentbase_p.cpp"
1217