kde-playground/kdepimlibs/akonadi/agentbase.cpp

1217 lines
40 KiB
C++
Raw Normal View History

/*
Copyright (c) 2006 Till Adam <adam@kde.org>
Copyright (c) 2007 Volker Krause <vkrause@kde.org>
Copyright (c) 2007 Bruno Virlet <bruno.virlet@gmail.com>
Copyright (c) 2008 Kevin Krammer <kevin.krammer@gmx.at>
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#include "agentbase.h"
#include "agentbase_p.h"
#include "agentmanager.h"
#include "changerecorder.h"
#include "controladaptor.h"
#include "dbusconnectionpool.h"
#include "itemfetchjob.h"
#include "kdepimlibs-version.h"
#include "monitor_p.h"
#include "servermanager_p.h"
#include "session.h"
#include "session_p.h"
#include "statusadaptor.h"
#include <kaboutdata.h>
#include <kcmdlineargs.h>
#include <kdebug.h>
#include <kglobal.h>
#include <klocalizedstring.h>
#include <kstandarddirs.h>
#include <Solid/PowerManagement>
#include <QtCore/QDir>
#include <QtCore/QSettings>
#include <QtCore/QTimer>
#include <QtDBus/QtDBus>
#include <QApplication>
#include <signal.h>
#include <stdlib.h>
#if defined __GLIBC__
# include <malloc.h> // for dumping memory information
#endif
//#define EXPERIMENTAL_INPROCESS_AGENTS 1
using namespace Akonadi;
static AgentBase *sAgentBase = 0;
AgentBase::Observer::Observer()
{
}
AgentBase::Observer::~Observer()
{
}
void AgentBase::Observer::itemAdded(const Item &item, const Collection &collection)
{
Q_UNUSED(item);
Q_UNUSED(collection);
if (sAgentBase != 0) {
sAgentBase->d_ptr->changeProcessed();
}
}
void AgentBase::Observer::itemChanged(const Item &item, const QSet<QByteArray> &partIdentifiers)
{
Q_UNUSED(item);
Q_UNUSED(partIdentifiers);
if (sAgentBase != 0) {
sAgentBase->d_ptr->changeProcessed();
}
}
void AgentBase::Observer::itemRemoved(const Item &item)
{
Q_UNUSED(item);
if (sAgentBase != 0) {
sAgentBase->d_ptr->changeProcessed();
}
}
void AgentBase::Observer::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent)
{
Q_UNUSED(collection);
Q_UNUSED(parent);
if (sAgentBase != 0) {
sAgentBase->d_ptr->changeProcessed();
}
}
void AgentBase::Observer::collectionChanged(const Collection &collection)
{
Q_UNUSED(collection);
if (sAgentBase != 0) {
sAgentBase->d_ptr->changeProcessed();
}
}
void AgentBase::Observer::collectionRemoved(const Collection &collection)
{
Q_UNUSED(collection);
if (sAgentBase != 0) {
sAgentBase->d_ptr->changeProcessed();
}
}
void AgentBase::ObserverV2::itemMoved(const Akonadi::Item &item, const Akonadi::Collection &source, const Akonadi::Collection &dest)
{
Q_UNUSED(item);
Q_UNUSED(source);
Q_UNUSED(dest);
if (sAgentBase != 0) {
sAgentBase->d_ptr->changeProcessed();
}
}
void AgentBase::ObserverV2::itemLinked(const Akonadi::Item &item, const Akonadi::Collection &collection)
{
Q_UNUSED(item);
Q_UNUSED(collection);
if (sAgentBase != 0) {
// not implementation, let's disconnect the signal to enable optimizations in Monitor
QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(itemLinked(Akonadi::Item,Akonadi::Collection)),
sAgentBase->d_ptr, SLOT(itemLinked(Akonadi::Item,Akonadi::Collection)));
sAgentBase->d_ptr->changeProcessed();
}
}
void AgentBase::ObserverV2::itemUnlinked(const Akonadi::Item &item, const Akonadi::Collection &collection)
{
Q_UNUSED(item);
Q_UNUSED(collection);
if (sAgentBase != 0) {
// not implementation, let's disconnect the signal to enable optimizations in Monitor
QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(itemUnlinked(Akonadi::Item,Akonadi::Collection)),
sAgentBase->d_ptr, SLOT(itemUnlinked(Akonadi::Item,Akonadi::Collection)));
sAgentBase->d_ptr->changeProcessed();
}
}
void AgentBase::ObserverV2::collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &source, const Akonadi::Collection &dest)
{
Q_UNUSED(collection);
Q_UNUSED(source);
Q_UNUSED(dest);
if (sAgentBase != 0) {
sAgentBase->d_ptr->changeProcessed();
}
}
void AgentBase::ObserverV2::collectionChanged(const Akonadi::Collection &collection, const QSet<QByteArray> &changedAttributes)
{
Q_UNUSED(changedAttributes);
collectionChanged(collection);
}
void AgentBase::ObserverV3::itemsFlagsChanged(const Akonadi::Item::List &items, const QSet< QByteArray > &addedFlags, const QSet< QByteArray > &removedFlags)
{
Q_UNUSED(items);
Q_UNUSED(addedFlags);
Q_UNUSED(removedFlags);
if (sAgentBase != 0) {
// not implementation, let's disconnect the signal to enable optimizations in Monitor
QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(itemsFlagsChanged(Akonadi::Item::List,QSet<QByteArray>,QSet<QByteArray>)),
sAgentBase->d_ptr, SLOT(itemsFlagsChanged(Akonadi::Item::List,QSet<QByteArray>,QSet<QByteArray>)));
sAgentBase->d_ptr->changeProcessed();
}
}
void AgentBase::ObserverV3::itemsMoved(const Akonadi::Item::List &items, const Collection &sourceCollection, const Collection &destinationCollection)
{
Q_UNUSED(items);
Q_UNUSED(sourceCollection);
Q_UNUSED(destinationCollection);
if (sAgentBase != 0) {
// not implementation, let's disconnect the signal to enable optimizations in Monitor
QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(itemsMoved(Akonadi::Item::List,Akonadi::Collection,Akonadi::Collection)),
sAgentBase->d_ptr, SLOT(itemsMoved(Akonadi::Item::List,Akonadi::Collection,Akonadi::Collection)));
sAgentBase->d_ptr->changeProcessed();
}
}
void AgentBase::ObserverV3::itemsRemoved(const Akonadi::Item::List &items)
{
Q_UNUSED(items);
if (sAgentBase != 0) {
// not implementation, let's disconnect the signal to enable optimizations in Monitor
QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(itemsRemoved(Akonadi::Item::List)),
sAgentBase->d_ptr, SLOT(itemsRemoved(Akonadi::Item::List)));
sAgentBase->d_ptr->changeProcessed();
}
}
void AgentBase::ObserverV3::itemsLinked(const Akonadi::Item::List &items, const Collection &collection)
{
Q_UNUSED(items);
Q_UNUSED(collection);
if (sAgentBase != 0) {
// not implementation, let's disconnect the signal to enable optimizations in Monitor
QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(itemsLinked(Akonadi::Item::List,Akonadi::Collection)),
sAgentBase->d_ptr, SLOT(itemsLinked(Akonadi::Item::List,Akonadi::Collection)));
sAgentBase->d_ptr->changeProcessed();
}
}
void AgentBase::ObserverV3::itemsUnlinked(const Akonadi::Item::List &items, const Collection &collection)
{
Q_UNUSED(items);
Q_UNUSED(collection)
if (sAgentBase != 0) {
// not implementation, let's disconnect the signal to enable optimizations in Monitor
QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(itemsUnlinked(Akonadi::Item::List,Akonadi::Collection)),
sAgentBase->d_ptr, SLOT(itemsUnlinked(Akonadi::Item::List,Akonadi::Collection)));
sAgentBase->d_ptr->changeProcessed();
}
}
void AgentBase::ObserverV4::tagAdded(const Tag &tag)
{
Q_UNUSED(tag);
if (sAgentBase != 0) {
// not implementation, let's disconnect the signal to enable optimization in Monitor
QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(tagAdded(Akonadi::Tag)),
sAgentBase->d_ptr, SLOT(tagAdded(Akonadi::Tag)));
sAgentBase->d_ptr->changeProcessed();
}
}
void AgentBase::ObserverV4::tagChanged(const Tag &tag)
{
Q_UNUSED(tag);
if (sAgentBase != 0) {
// not implementation, let's disconnect the signal to enable optimization in Monitor
QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(tagChanged(Akonadi::Tag)),
sAgentBase->d_ptr, SLOT(tagChanged(Akonadi::Tag)));
sAgentBase->d_ptr->changeProcessed();
}
}
void AgentBase::ObserverV4::tagRemoved(const Tag &tag)
{
Q_UNUSED(tag);
if (sAgentBase != 0) {
// not implementation, let's disconnect the signal to enable optimization in Monitor
QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(tagRemoved(Akonadi::Tag)),
sAgentBase->d_ptr, SLOT(tagRemoved(Akonadi::Tag)));
sAgentBase->d_ptr->changeProcessed();
}
}
void AgentBase::ObserverV4::itemsTagsChanged(const Item::List &items, const QSet<Tag> &addedTags, const QSet<Tag> &removedTags)
{
Q_UNUSED(items);
Q_UNUSED(addedTags);
Q_UNUSED(removedTags);
if (sAgentBase != 0) {
// not implementation, let's disconnect the signal to enable optimization in Monitor
QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(itemsTagsChanged(Akonadi::Item::List,QSet<Akonadi::Tag>,QSet<Akonadi::Tag>)),
sAgentBase->d_ptr, SLOT(itemsTagsChanged(Akonadi::Item::List,QSet<Akonadi::Tag>,QSet<Akonadi::Tag>)));
sAgentBase->d_ptr->changeProcessed();
}
}
//@cond PRIVATE
AgentBasePrivate::AgentBasePrivate(AgentBase *parent)
: q_ptr(parent)
, mDBusConnection(QString())
, mStatusCode(AgentBase::Idle)
, mProgress(0)
, mNeedsNetwork(false)
, mOnline(false)
, mDesiredOnlineState(false)
, mSettings(0)
, mChangeRecorder(0)
, mTracer(0)
, mObserver(0)
, mTemporaryOfflineTimer(0)
{
Internal::setClientType(Internal::Agent);
}
AgentBasePrivate::~AgentBasePrivate()
{
mChangeRecorder->setConfig(0);
delete mSettings;
}
void AgentBasePrivate::init()
{
Q_Q(AgentBase);
/**
* Create a default session for this process.
*/
SessionPrivate::createDefaultSession(mId.toLatin1());
if (QThread::currentThread() != QCoreApplication::instance()->thread()) {
mDBusConnection = QDBusConnection::connectToBus(QDBusConnection::SessionBus, q->identifier());
Q_ASSERT(mDBusConnection.isConnected());
}
mTracer = new org::freedesktop::Akonadi::Tracer(ServerManager::serviceName(ServerManager::Server),
QLatin1String("/tracing"),
DBusConnectionPool::threadConnection(), q);
new Akonadi__ControlAdaptor(q);
new Akonadi__StatusAdaptor(q);
if (!DBusConnectionPool::threadConnection().registerObject(QLatin1String("/"), q, QDBusConnection::ExportAdaptors)) {
q->error(i18n("Unable to register object at dbus: %1", DBusConnectionPool::threadConnection().lastError().message()));
}
mSettings = new QSettings(QString::fromLatin1("%1/agent_config_%2").arg(Internal::xdgSaveDir("config"), mId), QSettings::IniFormat);
mChangeRecorder = new ChangeRecorder(q);
mChangeRecorder->ignoreSession(Session::defaultSession());
mChangeRecorder->itemFetchScope().setCacheOnly(true);
mChangeRecorder->setConfig(mSettings);
mDesiredOnlineState = mSettings->value(QLatin1String("Agent/DesiredOnlineState"), true).toBool();
mOnline = mDesiredOnlineState;
// reinitialize the status message now that online state is available
mStatusMessage = defaultReadyMessage();
mName = mSettings->value(QLatin1String("Agent/Name")).toString();
if (mName.isEmpty()) {
mName = mSettings->value(QLatin1String("Resource/Name")).toString();
if (!mName.isEmpty()) {
mSettings->remove(QLatin1String("Resource/Name"));
mSettings->setValue(QLatin1String("Agent/Name"), mName);
}
}
connect(mChangeRecorder, SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection)),
SLOT(itemAdded(Akonadi::Item,Akonadi::Collection)));
connect(mChangeRecorder, SIGNAL(itemChanged(Akonadi::Item,QSet<QByteArray>)),
SLOT(itemChanged(Akonadi::Item,QSet<QByteArray>)));
connect(mChangeRecorder, SIGNAL(collectionAdded(Akonadi::Collection,Akonadi::Collection)),
SLOT(collectionAdded(Akonadi::Collection,Akonadi::Collection)));
connect(mChangeRecorder, SIGNAL(collectionChanged(Akonadi::Collection)),
SLOT(collectionChanged(Akonadi::Collection)));
connect(mChangeRecorder, SIGNAL(collectionChanged(Akonadi::Collection,QSet<QByteArray>)),
SLOT(collectionChanged(Akonadi::Collection,QSet<QByteArray>)));
connect(mChangeRecorder, SIGNAL(collectionMoved(Akonadi::Collection,Akonadi::Collection,Akonadi::Collection)),
SLOT(collectionMoved(Akonadi::Collection,Akonadi::Collection,Akonadi::Collection)));
connect(mChangeRecorder, SIGNAL(collectionRemoved(Akonadi::Collection)),
SLOT(collectionRemoved(Akonadi::Collection)));
connect(mChangeRecorder, SIGNAL(collectionSubscribed(Akonadi::Collection,Akonadi::Collection)),
SLOT(collectionSubscribed(Akonadi::Collection,Akonadi::Collection)));
connect(mChangeRecorder, SIGNAL(collectionUnsubscribed(Akonadi::Collection)),
SLOT(collectionUnsubscribed(Akonadi::Collection)));
connect(q, SIGNAL(status(int,QString)), q, SLOT(slotStatus(int,QString)));
connect(q, SIGNAL(percent(int)), q, SLOT(slotPercent(int)));
connect(q, SIGNAL(warning(QString)), q, SLOT(slotWarning(QString)));
connect(q, SIGNAL(error(QString)), q, SLOT(slotError(QString)));
connect(Solid::PowerManagement::notifier(), SIGNAL(resumingFromSuspend()), q, SLOT(slotResumedFromSuspend()));
// Use reference counting to allow agents to finish internal jobs when the
// agent is stopped.
KGlobal::ref();
if (QThread::currentThread() == QCoreApplication::instance()->thread()) {
KGlobal::setAllowQuit(true);
}
// disable session management
if (KApplication::kApplication()) {
KApplication::kApplication()->disableSessionManagement();
}
mResourceTypeName = AgentManager::self()->instance(mId).type().name();
setProgramName();
QTimer::singleShot(0, q, SLOT(delayedInit()));
}
void AgentBasePrivate::delayedInit()
{
Q_Q(AgentBase);
const QString serviceId = ServerManager::agentServiceName(ServerManager::Agent, mId);
if (!DBusConnectionPool::threadConnection().registerService(serviceId)) {
kFatal() << "Unable to register service" << serviceId << "at dbus:" << DBusConnectionPool::threadConnection().lastError().message();
}
q->setOnlineInternal(mDesiredOnlineState);
DBusConnectionPool::threadConnection().registerObject(QLatin1String("/Debug"), this, QDBusConnection::ExportScriptableSlots);
}
void AgentBasePrivate::setProgramName()
{
// ugly, really ugly, if you find another solution, change it and blame me for this code (Andras)
QString programName = mResourceTypeName;
if (!mName.isEmpty()) {
programName = i18nc("Name and type of Akonadi resource", "%1 of type %2", mName, mResourceTypeName) ;
}
const_cast<KAboutData *>(KGlobal::mainComponent().aboutData())->setProgramName(ki18n(programName.toUtf8()));
}
void AgentBasePrivate::itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection)
{
if (mObserver != 0) {
mObserver->itemAdded(item, collection);
}
}
void AgentBasePrivate::itemChanged(const Akonadi::Item &item, const QSet<QByteArray> &partIdentifiers)
{
if (mObserver != 0) {
mObserver->itemChanged(item, partIdentifiers);
}
}
void AgentBasePrivate::itemMoved(const Akonadi::Item &item, const Akonadi::Collection &source, const Akonadi::Collection &dest)
{
AgentBase::ObserverV2 *observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
if (mObserver) {
// inter-resource moves, requires we know which resources the source and destination are in though
if (!source.resource().isEmpty() && !dest.resource().isEmpty()) {
if (source.resource() != dest.resource()) {
if (source.resource() == q_ptr->identifier()) { // moved away from us
Akonadi::Item i(item);
i.setParentCollection(source);
mObserver->itemRemoved(i);
} else if (dest.resource() == q_ptr->identifier()) { // moved to us
mObserver->itemAdded(item, dest);
} else if (observer2) {
observer2->itemMoved(item, source, dest);
} else {
// not for us, not sure if we should get here at all
changeProcessed();
}
return;
}
}
// intra-resource move
if (observer2) {
observer2->itemMoved(item, source, dest);
} else {
// ### we cannot just call itemRemoved here as this will already trigger changeProcessed()
// so, just itemAdded() is good enough as no resource can have implemented intra-resource moves anyway
// without using ObserverV2
mObserver->itemAdded(item, dest);
// mObserver->itemRemoved( item );
}
}
}
void AgentBasePrivate::itemRemoved(const Akonadi::Item &item)
{
if (mObserver != 0) {
mObserver->itemRemoved(item);
}
}
void AgentBasePrivate::itemLinked(const Akonadi::Item &item, const Akonadi::Collection &collection)
{
AgentBase::ObserverV2 *observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
if (observer2) {
observer2->itemLinked(item, collection);
} else {
changeProcessed();
}
}
void AgentBasePrivate::itemUnlinked(const Akonadi::Item &item, const Akonadi::Collection &collection)
{
AgentBase::ObserverV2 *observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
if (observer2) {
observer2->itemUnlinked(item, collection);
} else {
changeProcessed();
}
}
void AgentBasePrivate::itemsFlagsChanged(const Akonadi::Item::List &items, const QSet<QByteArray> &addedFlags, const QSet<QByteArray> &removedFlags)
{
AgentBase::ObserverV3 *observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver);
if (observer3) {
observer3->itemsFlagsChanged(items, addedFlags, removedFlags);
} else {
Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available");
}
}
void AgentBasePrivate::itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &source, const Akonadi::Collection &destination)
{
AgentBase::ObserverV3 *observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver);
if (observer3) {
observer3->itemsMoved(items, source, destination);
} else {
Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available");
}
}
void AgentBasePrivate::itemsRemoved(const Akonadi::Item::List &items)
{
AgentBase::ObserverV3 *observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver);
if (observer3) {
observer3->itemsRemoved(items);
} else {
Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available");
}
}
void AgentBasePrivate::itemsLinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection)
{
if (!mObserver) {
changeProcessed();
return;
}
AgentBase::ObserverV3 *observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver);
if (observer3) {
observer3->itemsLinked(items, collection);
} else {
Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available");
}
}
void AgentBasePrivate::itemsUnlinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection)
{
if (!mObserver) {
return;
}
AgentBase::ObserverV3 *observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver);
if (observer3) {
observer3->itemsUnlinked(items, collection);
} else {
Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available");
}
}
void AgentBasePrivate::tagAdded(const Akonadi::Tag &tag)
{
if (!mObserver) {
return;
}
AgentBase::ObserverV4 *observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
if (observer4) {
observer4->tagAdded(tag);
} else {
changeProcessed();
}
}
void AgentBasePrivate::tagChanged(const Akonadi::Tag &tag)
{
if (!mObserver) {
return;
}
AgentBase::ObserverV4 *observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
if (observer4) {
observer4->tagChanged(tag);
} else {
changeProcessed();
}
}
void AgentBasePrivate::tagRemoved(const Akonadi::Tag &tag)
{
if (!mObserver) {
return;
}
AgentBase::ObserverV4 *observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
if (observer4) {
observer4->tagRemoved(tag);;
} else {
changeProcessed();
}
}
void AgentBasePrivate::itemsTagsChanged(const Akonadi::Item::List &items, const QSet<Akonadi::Tag> &addedTags, const QSet<Akonadi::Tag> &removedTags)
{
if (!mObserver) {
return;
}
AgentBase::ObserverV4 *observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
if (observer4) {
observer4->itemsTagsChanged(items, addedTags, removedTags);
} else {
changeProcessed();
}
}
void AgentBasePrivate::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent)
{
if (mObserver != 0) {
mObserver->collectionAdded(collection, parent);
}
}
void AgentBasePrivate::collectionChanged(const Akonadi::Collection &collection)
{
AgentBase::ObserverV2 *observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
if (mObserver != 0 && observer2 == 0) { // For ObserverV2 we use the variant with the part identifiers
mObserver->collectionChanged(collection);
}
}
void AgentBasePrivate::collectionChanged(const Akonadi::Collection &collection, const QSet<QByteArray> &changedAttributes)
{
AgentBase::ObserverV2 *observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
if (observer2 != 0) {
observer2->collectionChanged(collection, changedAttributes);
}
}
void AgentBasePrivate::collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &source, const Akonadi::Collection &dest)
{
AgentBase::ObserverV2 *observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
if (observer2) {
observer2->collectionMoved(collection, source, dest);
} else if (mObserver) {
// ### we cannot just call collectionRemoved here as this will already trigger changeProcessed()
// so, just collectionAdded() is good enough as no resource can have implemented intra-resource moves anyway
// without using ObserverV2
mObserver->collectionAdded(collection, dest);
} else {
changeProcessed();
}
}
void AgentBasePrivate::collectionRemoved(const Akonadi::Collection &collection)
{
if (mObserver != 0) {
mObserver->collectionRemoved(collection);
}
}
void AgentBasePrivate::collectionSubscribed(const Akonadi::Collection &collection, const Akonadi::Collection &parent)
{
Q_UNUSED(collection);
Q_UNUSED(parent);
changeProcessed();
}
void AgentBasePrivate::collectionUnsubscribed(const Akonadi::Collection &collection)
{
Q_UNUSED(collection);
changeProcessed();
}
void AgentBasePrivate::changeProcessed()
{
mChangeRecorder->changeProcessed();
QTimer::singleShot(0, mChangeRecorder, SLOT(replayNext()));
}
void AgentBasePrivate::slotStatus(int status, const QString &message)
{
mStatusMessage = message;
mStatusCode = 0;
switch (status) {
case AgentBase::Idle:
if (mStatusMessage.isEmpty()) {
mStatusMessage = defaultReadyMessage();
}
mStatusCode = 0;
break;
case AgentBase::Running:
if (mStatusMessage.isEmpty()) {
mStatusMessage = defaultSyncingMessage();
}
mStatusCode = 1;
break;
case AgentBase::Broken:
if (mStatusMessage.isEmpty()) {
mStatusMessage = defaultErrorMessage();
}
mStatusCode = 2;
break;
case AgentBase::NotConfigured:
if (mStatusMessage.isEmpty()) {
mStatusMessage = defaultUnconfiguredMessage();
}
mStatusCode = 3;
break;
default:
Q_ASSERT(!"Unknown status passed");
break;
}
}
void AgentBasePrivate::slotPercent(int progress)
{
mProgress = progress;
}
void AgentBasePrivate::slotWarning(const QString &message)
{
mTracer->warning(QString::fromLatin1("AgentBase(%1)").arg(mId), message);
}
void AgentBasePrivate::slotError(const QString &message)
{
mTracer->error(QString::fromLatin1("AgentBase(%1)").arg(mId), message);
}
void AgentBasePrivate::slotNetworkStatusChange(Solid::Networking::Status stat)
{
Q_UNUSED(stat);
Q_Q(AgentBase);
q->setOnlineInternal(mDesiredOnlineState);
}
void AgentBasePrivate::slotResumedFromSuspend()
{
if (mNeedsNetwork) {
slotNetworkStatusChange(Solid::Networking::status());
}
}
void AgentBasePrivate::slotTemporaryOfflineTimeout()
{
Q_Q(AgentBase);
q->setOnlineInternal(true);
}
QString AgentBasePrivate::dumpNotificationListToString() const
{
return mChangeRecorder->dumpNotificationListToString();
}
void AgentBasePrivate::dumpMemoryInfo() const
{
// Send it to stdout, so we can debug user problems.
// since you have to explicitely call this
// it won't flood users with release builds.
QTextStream stream(stdout);
stream << dumpMemoryInfoToString();
}
QString AgentBasePrivate::dumpMemoryInfoToString() const
{
// man mallinfo for more info
QString str;
#if defined __GLIBC__
struct mallinfo mi;
mi = mallinfo();
QTextStream stream(&str);
stream
<< "Total non-mmapped bytes (arena): " << mi.arena << '\n'
<< "# of free chunks (ordblks): " << mi.ordblks << '\n'
<< "# of free fastbin blocks (smblks>: " << mi.smblks << '\n'
<< "# of mapped regions (hblks): " << mi.hblks << '\n'
<< "Bytes in mapped regions (hblkhd): " << mi.hblkhd << '\n'
<< "Max. total allocated space (usmblks): " << mi.usmblks << '\n'
<< "Free bytes held in fastbins (fsmblks):" << mi.fsmblks << '\n'
<< "Total allocated space (uordblks): " << mi.uordblks << '\n'
<< "Total free space (fordblks): " << mi.fordblks << '\n'
<< "Topmost releasable block (keepcost): " << mi.keepcost << '\n';
#else
str = QLatin1String( "mallinfo() not supported" );
#endif
return str;
}
AgentBase::AgentBase(const QString &id)
: d_ptr(new AgentBasePrivate(this))
{
sAgentBase = this;
d_ptr->mId = id;
d_ptr->init();
}
AgentBase::AgentBase(AgentBasePrivate *d, const QString &id)
: d_ptr(d)
{
sAgentBase = this;
d_ptr->mId = id;
d_ptr->init();
}
AgentBase::~AgentBase()
{
delete d_ptr;
}
QString AgentBase::parseArguments(int argc, char **argv)
{
QString identifier;
if (argc < 3) {
kDebug() << "Not enough arguments passed...";
exit(1);
}
for (int i = 1; i < argc - 1; ++i) {
if (QLatin1String(argv[i]) == QLatin1String("--identifier")) {
identifier = QLatin1String(argv[i + 1]);
}
}
if (identifier.isEmpty()) {
kDebug() << "Identifier argument missing";
exit(1);
}
const QFileInfo fi(QString::fromLocal8Bit(argv[0]));
// strip off full path and possible .exe suffix
const QByteArray catalog = fi.baseName().toLatin1();
KCmdLineArgs::init(argc, argv, ServerManager::addNamespace(identifier).toLatin1(), catalog, ki18n("Akonadi Agent"), KDEPIMLIBS_VERSION,
ki18n("Akonadi Agent"));
KCmdLineOptions options;
options.add("identifier <argument>", ki18n("Agent identifier"));
KCmdLineArgs::addCmdLineOptions(options);
return identifier;
}
// @endcond
int AgentBase::init(AgentBase *r)
{
QApplication::setQuitOnLastWindowClosed(false);
KGlobal::locale()->insertCatalog(QLatin1String("libakonadi"));
int rv = kapp->exec();
delete r;
return rv;
}
int AgentBase::status() const
{
Q_D(const AgentBase);
return d->mStatusCode;
}
QString AgentBase::statusMessage() const
{
Q_D(const AgentBase);
return d->mStatusMessage;
}
int AgentBase::progress() const
{
Q_D(const AgentBase);
return d->mProgress;
}
QString AgentBase::progressMessage() const
{
Q_D(const AgentBase);
return d->mProgressMessage;
}
bool AgentBase::isOnline() const
{
Q_D(const AgentBase);
return d->mOnline;
}
void AgentBase::setNeedsNetwork(bool needsNetwork)
{
Q_D(AgentBase);
d->mNeedsNetwork = needsNetwork;
if (d->mNeedsNetwork) {
connect(Solid::Networking::notifier()
, SIGNAL(statusChanged(Solid::Networking::Status))
, this, SLOT(slotNetworkStatusChange(Solid::Networking::Status))
, Qt::UniqueConnection);
} else {
disconnect(Solid::Networking::notifier(), 0, 0, 0);
setOnlineInternal(d->mDesiredOnlineState);
}
}
void AgentBase::setOnline(bool state)
{
Q_D(AgentBase);
d->mDesiredOnlineState = state;
d->mSettings->setValue(QLatin1String("Agent/DesiredOnlineState"), state);
setOnlineInternal(state);
}
void AgentBase::setTemporaryOffline(int makeOnlineInSeconds)
{
Q_D(AgentBase);
// if not currently online, avoid bringing it online after the timeout
if (!d->mOnline) {
return;
}
setOnlineInternal(false);
if (!d->mTemporaryOfflineTimer) {
d->mTemporaryOfflineTimer = new QTimer(d);
d->mTemporaryOfflineTimer->setSingleShot(true);
connect(d->mTemporaryOfflineTimer, SIGNAL(timeout()), this, SLOT(slotTemporaryOfflineTimeout()));
}
d->mTemporaryOfflineTimer->setInterval(makeOnlineInSeconds * 1000);
d->mTemporaryOfflineTimer->start();
}
void AgentBase::setOnlineInternal( bool state )
{
Q_D(AgentBase);
if (state && d->mNeedsNetwork) {
const Solid::Networking::Status stat = Solid::Networking::status();
if (stat != Solid::Networking::Unknown && stat != Solid::Networking::Connected) {
//Don't go online if the resource needs network but there is none
state = false;
}
}
d->mOnline = state;
if (d->mTemporaryOfflineTimer) {
d->mTemporaryOfflineTimer->stop();
}
const QString newMessage = d->defaultReadyMessage();
if ( d->mStatusMessage != newMessage && d->mStatusCode != AgentBase::Broken ) {
emit status( d->mStatusCode, newMessage );
}
doSetOnline(state);
emit onlineChanged(state);
}
void AgentBase::doSetOnline(bool online)
{
Q_UNUSED(online);
}
void AgentBase::configure(WId windowId)
{
Q_UNUSED(windowId);
emit configurationDialogAccepted();
}
#ifdef Q_OS_WIN //krazy:exclude=cpp
void AgentBase::configure(qlonglong windowId)
{
configure(reinterpret_cast<WId>(windowId));
}
#endif
WId AgentBase::winIdForDialogs() const
{
const bool registered = DBusConnectionPool::threadConnection().interface()->isServiceRegistered(QLatin1String("org.freedesktop.akonaditray"));
if (!registered) {
return 0;
}
QDBusInterface dbus(QLatin1String("org.freedesktop.akonaditray"), QLatin1String("/Actions"),
QLatin1String("org.freedesktop.Akonadi.Tray"));
const QDBusMessage reply = dbus.call(QLatin1String("getWinId"));
if (reply.type() == QDBusMessage::ErrorMessage) {
return 0;
}
const WId winid = (WId)reply.arguments().at(0).toLongLong();
return winid;
}
void AgentBase::quit()
{
Q_D(AgentBase);
aboutToQuit();
if (d->mSettings) {
d->mChangeRecorder->setConfig(0);
d->mSettings->sync();
}
KGlobal::deref();
}
void AgentBase::aboutToQuit()
{
}
void AgentBase::cleanup()
{
Q_D(AgentBase);
// prevent the monitor from picking up deletion signals for our own data if we are a resource
// and thus avoid that we kill our own data as last act before our own death
d->mChangeRecorder->blockSignals(true);
aboutToQuit();
const QString fileName = d->mSettings->fileName();
/*
* First destroy the settings object...
*/
d->mChangeRecorder->setConfig(0);
delete d->mSettings;
d->mSettings = 0;
/*
* ... then remove the file from hd.
*/
QFile::remove(fileName);
/*
* ... and remove the changes file from hd.
*/
QFile::remove(fileName + QLatin1String("_changes.dat"));
/*
* ... and also remove the agent configuration file if there is one.
*/
QString configFile = KStandardDirs::locateLocal("config", config()->name());
QFile::remove(configFile);
KGlobal::deref();
}
void AgentBase::registerObserver(Observer *observer)
{
// TODO in theory we should re-connect change recorder signals here that we disconnected previously
d_ptr->mObserver = observer;
const bool hasObserverV3 = (dynamic_cast<AgentBase::ObserverV3 *>(d_ptr->mObserver) != 0);
const bool hasObserverV4 = (dynamic_cast<AgentBase::ObserverV4 *>(d_ptr->mObserver) != 0);
disconnect(d_ptr->mChangeRecorder, SIGNAL(tagAdded(Akonadi::Tag)),
d_ptr, SLOT(tagAdded(Akonadi::Tag)));
disconnect(d_ptr->mChangeRecorder, SIGNAL(tagChanged(Akonadi::Tag)),
d_ptr, SLOT(tagChanged(Akonadi::Tag)));
disconnect(d_ptr->mChangeRecorder, SIGNAL(tagRemoved(Akonadi::Tag)),
d_ptr, SLOT(tagRemoved(Akonadi::Tag)));
disconnect(d_ptr->mChangeRecorder, SIGNAL(itemsTagsChanged(Akonadi::Item::List,QSet<Akonadi::Tag>,QSet<Akonadi::Tag>)),
d_ptr, SLOT(itemsTagsChanged(Akonadi::Item::List,QSet<Akonadi::Tag>,QSet<Akonadi::Tag>)));
disconnect(d_ptr->mChangeRecorder, SIGNAL(itemsFlagsChanged(Akonadi::Item::List,QSet<QByteArray>,QSet<QByteArray>)),
d_ptr, SLOT(itemsFlagsChanged(Akonadi::Item::List,QSet<QByteArray>,QSet<QByteArray>)));
disconnect(d_ptr->mChangeRecorder, SIGNAL(itemsMoved(Akonadi::Item::List,Akonadi::Collection,Akonadi::Collection)),
d_ptr, SLOT(itemsMoved(Akonadi::Item::List,Akonadi::Collection,Akonadi::Collection)));
disconnect(d_ptr->mChangeRecorder, SIGNAL(itemsRemoved(Akonadi::Item::List)),
d_ptr, SLOT(itemsRemoved(Akonadi::Item::List)));
disconnect(d_ptr->mChangeRecorder, SIGNAL(itemsLinked(Akonadi::Item::List,Akonadi::Collection)),
d_ptr, SLOT(itemsLinked(Akonadi::Item::List,Akonadi::Collection)));
disconnect(d_ptr->mChangeRecorder, SIGNAL(itemsUnlinked(Akonadi::Item::List,Akonadi::Collection)),
d_ptr, SLOT(itemsUnlinked(Akonadi::Item::List,Akonadi::Collection)));
disconnect(d_ptr->mChangeRecorder, SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)),
d_ptr, SLOT(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)));
disconnect(d_ptr->mChangeRecorder, SIGNAL(itemRemoved(Akonadi::Item)),
d_ptr, SLOT(itemRemoved(Akonadi::Item)));
disconnect(d_ptr->mChangeRecorder, SIGNAL(itemLinked(Akonadi::Item,Akonadi::Collection)),
d_ptr, SLOT(itemLinked(Akonadi::Item,Akonadi::Collection)));
disconnect(d_ptr->mChangeRecorder, SIGNAL(itemUnlinked(Akonadi::Item,Akonadi::Collection)),
d_ptr, SLOT(itemUnlinked(Akonadi::Item,Akonadi::Collection)));
if (hasObserverV4) {
connect(d_ptr->mChangeRecorder, SIGNAL(tagAdded(Akonadi::Tag)),
d_ptr, SLOT(tagAdded(Akonadi::Tag)));
connect(d_ptr->mChangeRecorder, SIGNAL(tagChanged(Akonadi::Tag)),
d_ptr, SLOT(tagChanged(Akonadi::Tag)));
connect(d_ptr->mChangeRecorder, SIGNAL(tagRemoved(Akonadi::Tag)),
d_ptr, SLOT(tagRemoved(Akonadi::Tag)));
connect(d_ptr->mChangeRecorder, SIGNAL(itemsTagsChanged(Akonadi::Item::List,QSet<Akonadi::Tag>,QSet<Akonadi::Tag>)),
d_ptr, SLOT(itemsTagsChanged(Akonadi::Item::List,QSet<Akonadi::Tag>,QSet<Akonadi::Tag>)));
}
if (hasObserverV3) {
connect(d_ptr->mChangeRecorder, SIGNAL(itemsFlagsChanged(Akonadi::Item::List,QSet<QByteArray>,QSet<QByteArray>)),
d_ptr, SLOT(itemsFlagsChanged(Akonadi::Item::List,QSet<QByteArray>,QSet<QByteArray>)));
connect(d_ptr->mChangeRecorder, SIGNAL(itemsMoved(Akonadi::Item::List,Akonadi::Collection,Akonadi::Collection)),
d_ptr, SLOT(itemsMoved(Akonadi::Item::List,Akonadi::Collection,Akonadi::Collection)));
connect(d_ptr->mChangeRecorder, SIGNAL(itemsRemoved(Akonadi::Item::List)),
d_ptr, SLOT(itemsRemoved(Akonadi::Item::List)));
connect(d_ptr->mChangeRecorder, SIGNAL(itemsLinked(Akonadi::Item::List,Akonadi::Collection)),
d_ptr, SLOT(itemsLinked(Akonadi::Item::List,Akonadi::Collection)));
connect(d_ptr->mChangeRecorder, SIGNAL(itemsUnlinked(Akonadi::Item::List,Akonadi::Collection)),
d_ptr, SLOT(itemsUnlinked(Akonadi::Item::List,Akonadi::Collection)));
} else {
// V2 - don't connect these if we have V3
connect(d_ptr->mChangeRecorder, SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)),
d_ptr, SLOT(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)));
connect(d_ptr->mChangeRecorder, SIGNAL(itemRemoved(Akonadi::Item)),
d_ptr, SLOT(itemRemoved(Akonadi::Item)));
connect(d_ptr->mChangeRecorder, SIGNAL(itemLinked(Akonadi::Item,Akonadi::Collection)),
d_ptr, SLOT(itemLinked(Akonadi::Item,Akonadi::Collection)));
connect(d_ptr->mChangeRecorder, SIGNAL(itemUnlinked(Akonadi::Item,Akonadi::Collection)),
d_ptr, SLOT(itemUnlinked(Akonadi::Item,Akonadi::Collection)));
}
}
QString AgentBase::identifier() const
{
return d_ptr->mId;
}
void AgentBase::setAgentName(const QString &name)
{
Q_D(AgentBase);
if (name == d->mName) {
return;
}
// TODO: rename collection
d->mName = name;
if (d->mName.isEmpty() || d->mName == d->mId) {
d->mSettings->remove(QLatin1String("Resource/Name"));
d->mSettings->remove(QLatin1String("Agent/Name"));
} else
d->mSettings->setValue(QLatin1String("Agent/Name"), d->mName);
d->mSettings->sync();
d->setProgramName();
emit agentNameChanged(d->mName);
}
QString AgentBase::agentName() const
{
Q_D(const AgentBase);
if (d->mName.isEmpty()) {
return d->mId;
} else {
return d->mName;
}
}
void AgentBase::changeProcessed()
{
Q_D(AgentBase);
d->changeProcessed();
}
ChangeRecorder *AgentBase::changeRecorder() const
{
return d_ptr->mChangeRecorder;
}
KSharedConfigPtr AgentBase::config()
{
if (QCoreApplication::instance()->thread() == QThread::currentThread()) {
return KGlobal::config();
} else {
return componentData().config();
}
}
void AgentBase::abort()
{
emit abortRequested();
}
void AgentBase::reconfigure()
{
emit reloadConfiguration();
}
extern QThreadStorage<KComponentData *> s_agentComponentDatas;
KComponentData AgentBase::componentData()
{
if (QThread::currentThread() == QCoreApplication::instance()->thread()) {
if (s_agentComponentDatas.hasLocalData()) {
return *(s_agentComponentDatas.localData());
} else {
return KGlobal::mainComponent();
}
}
Q_ASSERT(s_agentComponentDatas.hasLocalData());
return *(s_agentComponentDatas.localData());
}
#include "moc_agentbase.cpp"
#include "moc_agentbase_p.cpp"