aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Vrátil <dvratil@kde.org>2016-08-15 22:43:24 (GMT)
committerDaniel Vrátil <dvratil@kde.org>2016-08-15 22:43:24 (GMT)
commit6eaeeb2bcfcb05478f6d2eda8e37e85e32fb4785 (patch)
tree22cf895819daa3d84696cc76bf2b433b941f2b6c
parent1bdde3b97122d93f99305fe3ec30755d0162bb8b (diff)
parentae660b94dbc6357d7b58eebeb1070e56b1d10546 (diff)
Merge current state of Notification Stream from dev/ntf-stream branch
There is a new dedicated socket for change notifications that the Server listens on. This socket is called Notification Bus and introduces a new way of handling change notifications on the Server. There is now only a single thread that handles all notification subscribers (instead of current one thread per subscriber), which should reduce memory footprint of the Server without impacting the performance because we don't need to handle multiple incomming request at once. Every Monitor creates a new connection to the Notification Bus and it now uses the Bus to manage its notification subscription settings using newly introduced Protocol commands. The Server uses the same connection to send change notifications back to the Monitor. Since we are using the Protocol to communicate over a dedicated socket, we now have a dedicated Protocol command for each notification type (Item, Collection, ...). All this lays the foundation for further work on Notification Payloads (sending the relevant Item/Collection/... as part of the Notification) and Server-side Change Recording.
-rw-r--r--autotests/libs/CMakeLists.txt2
-rw-r--r--autotests/libs/fakeentitycache.h24
-rw-r--r--autotests/libs/inspectablechangerecorder.h9
-rw-r--r--autotests/libs/inspectablemonitor.h9
-rw-r--r--autotests/libs/monitornotificationtest.cpp30
-rw-r--r--autotests/libs/monitortest.cpp43
-rw-r--r--autotests/private/notificationmessagetest.cpp64
-rw-r--r--autotests/private/notificationmessagetest.h1
-rw-r--r--autotests/private/protocoltest.cpp12
-rw-r--r--autotests/server/akappendhandlertest.cpp30
-rw-r--r--autotests/server/collectionreferencetest.cpp38
-rw-r--r--autotests/server/createhandlertest.cpp31
-rw-r--r--autotests/server/fakeakonadiserver.cpp38
-rw-r--r--autotests/server/fakeakonadiserver.h4
-rw-r--r--autotests/server/linkhandlertest.cpp56
-rw-r--r--autotests/server/modifyhandlertest.cpp81
-rw-r--r--autotests/server/notificationmanagertest.cpp306
-rw-r--r--autotests/server/relationhandlertest.cpp71
-rw-r--r--autotests/server/taghandlertest.cpp58
-rw-r--r--src/core/CMakeLists.txt6
-rw-r--r--src/core/changenotificationdependenciesfactory.cpp55
-rw-r--r--src/core/changenotificationdependenciesfactory_p.h5
-rw-r--r--src/core/changerecorder_p.cpp743
-rw-r--r--src/core/changerecorder_p.h44
-rw-r--r--src/core/connection.cpp (renamed from src/core/connectionthread.cpp)76
-rw-r--r--src/core/connection_p.h (renamed from src/core/connectionthread_p.h)24
-rw-r--r--src/core/jobs/collectionmovejob.cpp3
-rw-r--r--src/core/monitor.cpp93
-rw-r--r--src/core/monitor.h7
-rw-r--r--src/core/monitor_p.cpp975
-rw-r--r--src/core/monitor_p.h34
-rw-r--r--src/core/notificationbus_p.cpp86
-rw-r--r--src/core/session.cpp57
-rw-r--r--src/core/session_p.h9
-rw-r--r--src/core/sessionthread.cpp87
-rw-r--r--src/core/sessionthread_p.h54
-rw-r--r--src/core/subscriptionmonitor.cpp247
-rw-r--r--src/core/subscriptionmonitor.h123
-rw-r--r--src/private/protocol.cpp2079
-rw-r--r--src/private/protocol_p.h538
-rw-r--r--src/server/CMakeLists.txt6
-rw-r--r--src/server/aklocalserver.cpp33
-rw-r--r--src/server/aklocalserver.h (renamed from src/core/notificationbus_p.h)38
-rw-r--r--src/server/akonadi.cpp125
-rw-r--r--src/server/akonadi.h27
-rw-r--r--src/server/connection.cpp45
-rw-r--r--src/server/connection.h5
-rw-r--r--src/server/handler.cpp57
-rw-r--r--src/server/handler/login.cpp4
-rw-r--r--src/server/notificationmanager.cpp229
-rw-r--r--src/server/notificationmanager.h82
-rw-r--r--src/server/notificationsource.cpp483
-rw-r--r--src/server/notificationsource.h156
-rw-r--r--src/server/notificationsubscriber.cpp597
-rw-r--r--src/server/notificationsubscriber.h102
-rw-r--r--src/server/search/searchmanager.cpp6
-rw-r--r--src/server/storage/datastore.cpp6
-rw-r--r--src/server/storage/notificationcollector.cpp140
-rw-r--r--src/server/storage/notificationcollector.h14
-rw-r--r--src/shared/aktest.h11
60 files changed, 5598 insertions, 2820 deletions
diff --git a/autotests/libs/CMakeLists.txt b/autotests/libs/CMakeLists.txt
index 27aa258..768b031 100644
--- a/autotests/libs/CMakeLists.txt
+++ b/autotests/libs/CMakeLists.txt
@@ -123,7 +123,7 @@ add_akonadi_isolated_test(itemstoretest.cpp)
add_akonadi_isolated_test(itemdeletetest.cpp)
add_akonadi_isolated_test(entitycachetest.cpp)
add_akonadi_isolated_test(monitortest.cpp)
-add_akonadi_isolated_test_advanced(monitorfiltertest.cpp "" "KF5::AkonadiPrivate")
+#add_akonadi_isolated_test_advanced(monitorfiltertest.cpp "" "KF5::AkonadiPrivate")
add_akonadi_isolated_test(searchjobtest.cpp)
add_akonadi_isolated_test(changerecordertest.cpp)
add_akonadi_isolated_test(resourcetest.cpp)
diff --git a/autotests/libs/fakeentitycache.h b/autotests/libs/fakeentitycache.h
index b06f29e..b12b214 100644
--- a/autotests/libs/fakeentitycache.h
+++ b/autotests/libs/fakeentitycache.h
@@ -22,13 +22,12 @@
#include "monitor_p.h"
#include "notificationsource_p.h"
-#include "notificationbus_p.h"
-
#include "collectionfetchscope.h"
#include "itemfetchscope.h"
#include "akonaditestfake_export.h"
#include "private/protocol_p.h"
+
template<typename T, typename Cache>
class FakeEntityCache : public Cache
{
@@ -122,25 +121,27 @@ public Q_SLOTS:
}
};
-class AKONADITESTFAKE_EXPORT FakeNotificationBus : public QObject
+class AKONADITESTFAKE_EXPORT FakeNotificationConnection : public Akonadi::Connection
{
Q_OBJECT
public:
- explicit FakeNotificationBus(QObject *parent = Q_NULLPTR)
- : QObject(parent)
+ explicit FakeNotificationConnection(QObject *parent = Q_NULLPTR)
+ : Connection(Connection::NotificationConnection, "testConn", parent)
{}
- virtual ~FakeNotificationBus()
+ virtual ~FakeNotificationConnection()
{}
void emitNotify(const Akonadi::Protocol::ChangeNotification &ntf)
{
- Q_EMIT notify(ntf);
+ Q_EMIT commandReceived(3, ntf);
}
+ /*
Q_SIGNALS:
void notify(const Akonadi::Protocol::ChangeNotification &ntf);
+ */
};
class FakeMonitorDependeciesFactory : public Akonadi::ChangeNotificationDependenciesFactory
@@ -154,13 +155,8 @@ public:
{
}
- Akonadi::NotificationSource *createNotificationSource(QObject *parent) Q_DECL_OVERRIDE {
- return new Akonadi::NotificationSource(new FakeNotificationSource(parent));
- }
-
- QObject *createNotificationBus(QObject *parent, Akonadi::NotificationSource *source) Q_DECL_OVERRIDE {
- Q_UNUSED(source);
- return new FakeNotificationBus(parent);
+ Akonadi::Connection *createNotificationConnection(Akonadi::Session *parent) Q_DECL_OVERRIDE {
+ return new FakeNotificationConnection(parent);
}
Akonadi::CollectionCache *createCollectionCache(int maxCapacity, Akonadi::Session *session) Q_DECL_OVERRIDE {
diff --git a/autotests/libs/inspectablechangerecorder.h b/autotests/libs/inspectablechangerecorder.h
index 2f3e606..a742350 100644
--- a/autotests/libs/inspectablechangerecorder.h
+++ b/autotests/libs/inspectablechangerecorder.h
@@ -50,14 +50,9 @@ class AKONADITESTFAKE_EXPORT InspectableChangeRecorder : public Akonadi::ChangeR
public:
InspectableChangeRecorder(FakeMonitorDependeciesFactory *dependenciesFactory, QObject *parent = Q_NULLPTR);
- FakeNotificationSource *notifier() const
+ FakeNotificationConnection *notificationConnection() const
{
- return qobject_cast<FakeNotificationSource *>(d_ptr->notificationSource->source());
- }
-
- FakeNotificationBus *notificationBus() const
- {
- return qobject_cast<FakeNotificationBus *>(d_ptr->notificationBus);
+ return qobject_cast<FakeNotificationConnection *>(d_ptr->ntfConnection);
}
QQueue<Akonadi::Protocol::ChangeNotification> pendingNotifications() const
diff --git a/autotests/libs/inspectablemonitor.h b/autotests/libs/inspectablemonitor.h
index 0528cf3..68c6567 100644
--- a/autotests/libs/inspectablemonitor.h
+++ b/autotests/libs/inspectablemonitor.h
@@ -50,14 +50,9 @@ class AKONADITESTFAKE_EXPORT InspectableMonitor : public Akonadi::Monitor
public:
InspectableMonitor(FakeMonitorDependeciesFactory *dependenciesFactory, QObject *parent = Q_NULLPTR);
- FakeNotificationSource *notifier() const
+ FakeNotificationConnection *notificationConnection() const
{
- return qobject_cast<FakeNotificationSource *>(d_ptr->notificationSource->source());
- }
-
- FakeNotificationBus *notificationBus() const
- {
- return qobject_cast<FakeNotificationBus *>(d_ptr->notificationBus);
+ return qobject_cast<FakeNotificationConnection *>(d_ptr->ntfConnection);
}
QQueue<Akonadi::Protocol::ChangeNotification> pendingNotifications() const
diff --git a/autotests/libs/monitornotificationtest.cpp b/autotests/libs/monitornotificationtest.cpp
index 983c5b4..073ed01 100644
--- a/autotests/libs/monitornotificationtest.cpp
+++ b/autotests/libs/monitornotificationtest.cpp
@@ -93,6 +93,9 @@ void MonitorNotificationTest::testSingleMessage_impl(MonitorImpl *monitor, FakeC
{
Q_UNUSED(itemCache)
+ // Workaround for the QTimer::singleShot() in fake monitors to happen
+ QTest::qWait(10);
+
monitor->setSession(m_fakeSession);
monitor->fetchCollection(true);
@@ -101,11 +104,10 @@ void MonitorNotificationTest::testSingleMessage_impl(MonitorImpl *monitor, FakeC
Collection parent(1);
Collection added(2);
- Protocol::ChangeNotification msg;
+ Protocol::CollectionChangeNotification msg;
msg.setParentCollection(parent.id());
- msg.setOperation(Protocol::ChangeNotification::Add);
- msg.setType(Protocol::ChangeNotification::Collections);
- msg.addEntity(added.id());
+ msg.setOperation(Protocol::CollectionChangeNotification::Add);
+ msg.setId(added.id());
QHash<Collection::Id, Collection> data;
data.insert(parent.id(), parent);
@@ -116,7 +118,7 @@ void MonitorNotificationTest::testSingleMessage_impl(MonitorImpl *monitor, FakeC
QVERIFY(monitor->pipeline().isEmpty());
QVERIFY(monitor->pendingNotifications().isEmpty());
- monitor->notificationBus()->emitNotify(msg);
+ monitor->notificationConnection()->emitNotify(msg);
QCOMPARE(monitor->pipeline().size(), 1);
QVERIFY(monitor->pendingNotifications().isEmpty());
@@ -169,11 +171,10 @@ void MonitorNotificationTest::testFillPipeline_impl(MonitorImpl *monitor, FakeCo
Collection parent(i++);
Collection added(i++);
- Protocol::ChangeNotification msg;
+ Protocol::CollectionChangeNotification msg;
msg.setParentCollection(parent.id());
- msg.setOperation(Protocol::ChangeNotification::Add);
- msg.setType(Protocol::ChangeNotification::Collections);
- msg.addEntity(added.id());
+ msg.setOperation(Protocol::CollectionChangeNotification::Add);
+ msg.setId(added.id());
data.insert(parent.id(), parent);
data.insert(added.id(), added);
@@ -185,7 +186,7 @@ void MonitorNotificationTest::testFillPipeline_impl(MonitorImpl *monitor, FakeCo
QVERIFY(monitor->pendingNotifications().isEmpty());
Q_FOREACH (const Protocol::ChangeNotification &ntf, list) {
- monitor->notificationBus()->emitNotify(ntf);
+ monitor->notificationConnection()->emitNotify(ntf);
}
QCOMPARE(monitor->pipeline().size(), 5);
@@ -243,11 +244,10 @@ void MonitorNotificationTest::testMonitor_impl(MonitorImpl *monitor, FakeCollect
while (i < 8) {
Collection added(i++);
- Protocol::ChangeNotification msg;
+ Protocol::CollectionChangeNotification msg;
msg.setParentCollection(i % 2 ? 2 : added.id() - 1);
- msg.setOperation(Protocol::ChangeNotification::Add);
- msg.setType(Protocol::ChangeNotification::Collections);
- msg.addEntity(added.id());
+ msg.setOperation(Protocol::CollectionChangeNotification::Add);
+ msg.setId(added.id());
list << msg;
}
@@ -272,7 +272,7 @@ void MonitorNotificationTest::testMonitor_impl(MonitorImpl *monitor, FakeCollect
QVERIFY(monitor->pendingNotifications().isEmpty());
Q_FOREACH (const Protocol::ChangeNotification &ntf, list) {
- monitor->notificationBus()->emitNotify(ntf);
+ monitor->notificationConnection()->emitNotify(ntf);
}
// Collection 6 is not notified, because Collection 5 has held up the pipeline
diff --git a/autotests/libs/monitortest.cpp b/autotests/libs/monitortest.cpp
index 2c27dd1..cccd5ce 100644
--- a/autotests/libs/monitortest.cpp
+++ b/autotests/libs/monitortest.cpp
@@ -139,7 +139,9 @@ void MonitorTest::testMonitor()
AKVERIFYEXEC(create);
monitorCol = create->collection();
QVERIFY(monitorCol.isValid());
- QVERIFY(AkonadiTest::akWaitForSignal(monitor, SIGNAL(collectionAdded(Akonadi::Collection,Akonadi::Collection)), 1000));
+ if (caddspy.isEmpty()) {
+ QVERIFY(AkonadiTest::akWaitForSignal(monitor, SIGNAL(collectionAdded(Akonadi::Collection,Akonadi::Collection)), 6000));
+ }
QCOMPARE(caddspy.count(), 1);
QList<QVariant> arg = caddspy.takeFirst();
@@ -169,8 +171,13 @@ void MonitorTest::testMonitor()
AKVERIFYEXEC(append);
Item monitorRef = append->item();
QVERIFY(monitorRef.isValid());
- QVERIFY(AkonadiTest::akWaitForSignal(monitor, SIGNAL(collectionStatisticsChanged(Akonadi::Collection::Id,Akonadi::CollectionStatistics)), 1000));
+ if (cstatspy.isEmpty()) {
+ QVERIFY(AkonadiTest::akWaitForSignal(monitor, SIGNAL(collectionStatisticsChanged(Akonadi::Collection::Id,Akonadi::CollectionStatistics)), 1000));
+ }
+ for (int i = 0; i < cstatspy.count(); i++) {
+ qDebug() << "CSTAT" << cstatspy[i][0].toLongLong() << cstatspy[i][1].value<Akonadi::CollectionStatistics>().count();
+ }
QCOMPARE(cstatspy.count(), 1);
arg = cstatspy.takeFirst();
QCOMPARE(arg.at(0).value<Akonadi::Collection::Id>(), monitorCol.id());
@@ -197,7 +204,9 @@ void MonitorTest::testMonitor()
item.setPayload<QByteArray>("some new content");
ItemModifyJob *store = new ItemModifyJob(item, this);
AKVERIFYEXEC(store);
- QVERIFY(AkonadiTest::akWaitForSignal(monitor, SIGNAL(collectionStatisticsChanged(Akonadi::Collection::Id,Akonadi::CollectionStatistics)), 1000));
+ if (cstatspy.isEmpty()) {
+ QVERIFY(AkonadiTest::akWaitForSignal(monitor, SIGNAL(collectionStatisticsChanged(Akonadi::Collection::Id,Akonadi::CollectionStatistics)), 1000));
+ }
QCOMPARE(cstatspy.count(), 1);
arg = cstatspy.takeFirst();
@@ -225,7 +234,9 @@ void MonitorTest::testMonitor()
// move an item
ItemMoveJob *move = new ItemMoveJob(item, res3);
AKVERIFYEXEC(move);
- QVERIFY(AkonadiTest::akWaitForSignal(monitor, SIGNAL(collectionStatisticsChanged(Akonadi::Collection::Id,Akonadi::CollectionStatistics)), 1000));
+ if (cstatspy.isEmpty()) {
+ QVERIFY(AkonadiTest::akWaitForSignal(monitor, SIGNAL(collectionStatisticsChanged(Akonadi::Collection::Id,Akonadi::CollectionStatistics)), 1000));
+ }
QCOMPARE(cstatspy.count(), 2);
// NOTE: We don't make any assumptions about the order of the collectionStatisticsChanged
// signals, they seem to arrive in random order
@@ -257,7 +268,9 @@ void MonitorTest::testMonitor()
// delete an item
ItemDeleteJob *del = new ItemDeleteJob(monitorRef, this);
AKVERIFYEXEC(del);
- QVERIFY(AkonadiTest::akWaitForSignal(monitor, SIGNAL(collectionStatisticsChanged(Akonadi::Collection::Id,Akonadi::CollectionStatistics)), 1000));
+ if (cstatspy.isEmpty()) {
+ QVERIFY(AkonadiTest::akWaitForSignal(monitor, SIGNAL(collectionStatisticsChanged(Akonadi::Collection::Id,Akonadi::CollectionStatistics)), 1000));
+ }
QCOMPARE(cstatspy.count(), 1);
arg = cstatspy.takeFirst();
@@ -290,7 +303,9 @@ void MonitorTest::testMonitor()
subscribeJob->unsubscribe(Collection::List() << subCollection);
AKVERIFYEXEC(subscribeJob);
// Wait for unsubscribed signal, it goes after changed, so we can check for both
- QVERIFY(AkonadiTest::akWaitForSignal(monitor, SIGNAL(collectionUnsubscribed(Akonadi::Collection)), 1000));
+ if (cUnsubscribedSpy.isEmpty()) {
+ QVERIFY(AkonadiTest::akWaitForSignal(monitor, SIGNAL(collectionUnsubscribed(Akonadi::Collection)), 1000));
+ }
QCOMPARE(cmodspy.size(), 1);
arg = cmodspy.takeFirst();
col = arg.at(0).value<Collection>();
@@ -306,7 +321,9 @@ void MonitorTest::testMonitor()
subscribeJob->subscribe(Collection::List() << subCollection);
AKVERIFYEXEC(subscribeJob);
// Wait for subscribed signal, it goes after changed, so we can check for both
- QVERIFY(AkonadiTest::akWaitForSignal(monitor, SIGNAL(collectionSubscribed(Akonadi::Collection,Akonadi::Collection)), 1000));
+ if (cSubscribedSpy.isEmpty()) {
+ QVERIFY(AkonadiTest::akWaitForSignal(monitor, SIGNAL(collectionSubscribed(Akonadi::Collection,Akonadi::Collection)), 1000));
+ }
QCOMPARE(cmodspy.size(), 1);
arg = cmodspy.takeFirst();
col = arg.at(0).value<Collection>();
@@ -336,7 +353,9 @@ void MonitorTest::testMonitor()
monitorCol.setName(QStringLiteral("changed name"));
CollectionModifyJob *mod = new CollectionModifyJob(monitorCol, this);
AKVERIFYEXEC(mod);
- QVERIFY(AkonadiTest::akWaitForSignal(monitor, SIGNAL(collectionChanged(Akonadi::Collection)), 1000));
+ if (cmodspy.isEmpty()) {
+ QVERIFY(AkonadiTest::akWaitForSignal(monitor, SIGNAL(collectionChanged(Akonadi::Collection)), 1000));
+ }
QCOMPARE(cmodspy.count(), 1);
arg = cmodspy.takeFirst();
@@ -361,7 +380,9 @@ void MonitorTest::testMonitor()
Collection dest = Collection(collectionIdFromPath(QStringLiteral("res1/foo")));
CollectionMoveJob *cmove = new CollectionMoveJob(monitorCol, dest, this);
AKVERIFYEXEC(cmove);
- QVERIFY(AkonadiTest::akWaitForSignal(monitor, SIGNAL(collectionMoved(Akonadi::Collection,Akonadi::Collection,Akonadi::Collection)), 1000));
+ if (cmvspy.isEmpty()) {
+ QVERIFY(AkonadiTest::akWaitForSignal(monitor, SIGNAL(collectionMoved(Akonadi::Collection,Akonadi::Collection,Akonadi::Collection)), 1000));
+ }
QCOMPARE(cmvspy.count(), 1);
arg = cmvspy.takeFirst();
@@ -390,7 +411,9 @@ void MonitorTest::testMonitor()
// delete a collection
CollectionDeleteJob *cdel = new CollectionDeleteJob(monitorCol, this);
AKVERIFYEXEC(cdel);
- QVERIFY(AkonadiTest::akWaitForSignal(monitor, SIGNAL(collectionRemoved(Akonadi::Collection)), 1000));
+ if (crmspy.isEmpty()) {
+ QVERIFY(AkonadiTest::akWaitForSignal(monitor, SIGNAL(collectionRemoved(Akonadi::Collection)), 1000));
+ }
QCOMPARE(crmspy.count(), 1);
arg = crmspy.takeFirst();
diff --git a/autotests/private/notificationmessagetest.cpp b/autotests/private/notificationmessagetest.cpp
index 759fd71..4a93d3c 100644
--- a/autotests/private/notificationmessagetest.cpp
+++ b/autotests/private/notificationmessagetest.cpp
@@ -31,75 +31,63 @@ using namespace Akonadi::Protocol;
void NotificationMessageTest::testCompress()
{
ChangeNotification::List list;
- ChangeNotification msg;
- msg.setType(ChangeNotification::Collections);
- msg.setOperation(ChangeNotification::Add);
+ CollectionChangeNotification msg;
+ msg.setOperation(CollectionChangeNotification::Add);
- ChangeNotification::appendAndCompress(list, msg);
+ CollectionChangeNotification::appendAndCompress(list, msg);
QCOMPARE(list.count(), 1);
- msg.setOperation(ChangeNotification::Modify);
- ChangeNotification::appendAndCompress(list, msg);
+ msg.setOperation(CollectionChangeNotification::Modify);
+ CollectionChangeNotification::appendAndCompress(list, msg);
QCOMPARE(list.count(), 1);
- QCOMPARE(list.first().operation(), ChangeNotification::Add);
+ QCOMPARE(static_cast<CollectionChangeNotification&>(list.first()).operation(), CollectionChangeNotification::Add);
- msg.setOperation(ChangeNotification::Remove);
- ChangeNotification::appendAndCompress(list, msg);
+ msg.setOperation(CollectionChangeNotification::Remove);
+ CollectionChangeNotification::appendAndCompress(list, msg);
QCOMPARE(list.count(), 2);
}
void NotificationMessageTest::testCompress2()
{
ChangeNotification::List list;
- ChangeNotification msg;
- msg.setType(ChangeNotification::Collections);
- msg.setOperation(ChangeNotification::Modify);
+ CollectionChangeNotification msg;
+ msg.setOperation(CollectionChangeNotification::Modify);
- ChangeNotification::appendAndCompress(list, msg);
+ CollectionChangeNotification::appendAndCompress(list, msg);
QCOMPARE(list.count(), 1);
- msg.setOperation(ChangeNotification::Remove);
- ChangeNotification::appendAndCompress(list, msg);
+ msg.setOperation(CollectionChangeNotification::Remove);
+ CollectionChangeNotification::appendAndCompress(list, msg);
QCOMPARE(list.count(), 2);
- QCOMPARE(list.first().operation(), ChangeNotification::Modify);
- QCOMPARE(list.last().operation(), ChangeNotification::Remove);
+ QCOMPARE(static_cast<CollectionChangeNotification&>(list.first()).operation(), CollectionChangeNotification::Modify);
+ QCOMPARE(static_cast<CollectionChangeNotification&>(list.last()).operation(), CollectionChangeNotification::Remove);
}
void NotificationMessageTest::testCompress3()
{
ChangeNotification::List list;
- ChangeNotification msg;
- msg.setType(ChangeNotification::Collections);
- msg.setOperation(ChangeNotification::Modify);
+ CollectionChangeNotification msg;
+ msg.setOperation(CollectionChangeNotification::Modify);
- ChangeNotification::appendAndCompress(list, msg);
+ CollectionChangeNotification::appendAndCompress(list, msg);
QCOMPARE(list.count(), 1);
- ChangeNotification::appendAndCompress(list, msg);
+ CollectionChangeNotification::appendAndCompress(list, msg);
QCOMPARE(list.count(), 1);
}
-void NotificationMessageTest::testPartModificationMerge_data()
-{
- QTest::addColumn<ChangeNotification::Type>("type");
- QTest::newRow("collection") << ChangeNotification::Collections;
-}
-
void NotificationMessageTest::testPartModificationMerge()
{
- QFETCH(ChangeNotification::Type, type);
-
ChangeNotification::List list;
- ChangeNotification msg;
- msg.setType(type);
- msg.setOperation(ChangeNotification::Modify);
- msg.setItemParts(QSet<QByteArray>() << "PART1");
+ CollectionChangeNotification msg;
+ msg.setOperation(CollectionChangeNotification::Modify);
+ msg.setChangedParts(QSet<QByteArray>() << "PART1");
- ChangeNotification::appendAndCompress(list, msg);
+ CollectionChangeNotification::appendAndCompress(list, msg);
QCOMPARE(list.count(), 1);
- msg.setItemParts(QSet<QByteArray>() << "PART2");
- ChangeNotification::appendAndCompress(list, msg);
+ msg.setChangedParts(QSet<QByteArray>() << "PART2");
+ CollectionChangeNotification::appendAndCompress(list, msg);
QCOMPARE(list.count(), 1);
- QCOMPARE(list.first().itemParts(), (QSet<QByteArray>() << "PART1" << "PART2"));
+ QCOMPARE(static_cast<CollectionChangeNotification&>(list.first()).changedParts(), (QSet<QByteArray>() << "PART1" << "PART2"));
}
diff --git a/autotests/private/notificationmessagetest.h b/autotests/private/notificationmessagetest.h
index 3f07090..aa244ea 100644
--- a/autotests/private/notificationmessagetest.h
+++ b/autotests/private/notificationmessagetest.h
@@ -29,7 +29,6 @@ private Q_SLOTS:
void testCompress();
void testCompress2();
void testCompress3();
- void testPartModificationMerge_data();
void testPartModificationMerge();
};
diff --git a/autotests/private/protocoltest.cpp b/autotests/private/protocoltest.cpp
index 17bb636..61c6c91 100644
--- a/autotests/private/protocoltest.cpp
+++ b/autotests/private/protocoltest.cpp
@@ -110,8 +110,14 @@ void ProtocolTest::testFactory_data()
QTest::newRow("selectResource resp") << Command::SelectResource << true << true;
QTest::newRow("streamPayload cmd") << Command::StreamPayload << false << true;
QTest::newRow("streamPayload resp") << Command::StreamPayload << true << true;
- QTest::newRow("changeNotification cmd") << Command::ChangeNotification << false << true;
- QTest::newRow("changeNotification resp") << Command::ChangeNotification << true << false;
+ QTest::newRow("itemChangeNotification cmd") << Command::ItemChangeNotification << false << true;
+ QTest::newRow("itemChangeNotification resp") << Command::ItemChangeNotification << true << false;
+ QTest::newRow("collectionChangeNotification cmd") << Command::CollectionChangeNotification << false << true;
+ QTest::newRow("collectionChangeNotification resp") << Command::CollectionChangeNotification << true << false;
+ QTest::newRow("tagChangeNotification cmd") << Command::TagChangeNotification << false << true;
+ QTest::newRow("tagChangENotification resp") << Command::TagChangeNotification << true << false;
+ QTest::newRow("relationChangeNotification cmd") << Command::RelationChangeNotification << false << true;
+ QTest::newRow("relationChangeNotification resp") << Command::RelationChangeNotification << true << false;
QTest::newRow("_responseBit cmd") << Command::_ResponseBit << false << false;
QTest::newRow("_responseBit resp") << Command::_ResponseBit << true << false;
}
@@ -488,13 +494,11 @@ void ProtocolTest::testLoginCommand()
QVERIFY(!in.isResponse());
QVERIFY(in.isValid());
in.setSessionId("MySession-123-notifications");
- in.setSessionMode(LoginCommand::NotificationBus);
const LoginCommand out = serializeAndDeserialize(in);
QVERIFY(out.isValid());
QVERIFY(!out.isResponse());
QCOMPARE(out.sessionId(), QByteArray("MySession-123-notifications"));
- QCOMPARE(out.sessionMode(), LoginCommand::NotificationBus);
QCOMPARE(out, in);
const bool notEquals = (out != in);
QVERIFY(!notEquals);
diff --git a/autotests/server/akappendhandlertest.cpp b/autotests/server/akappendhandlertest.cpp
index 37223ba..d0841ee 100644
--- a/autotests/server/akappendhandlertest.cpp
+++ b/autotests/server/akappendhandlertest.cpp
@@ -41,6 +41,7 @@ Q_DECLARE_METATYPE(PimItem)
Q_DECLARE_METATYPE(QVector<Flag>)
Q_DECLARE_METATYPE(QVector<FakePart>)
Q_DECLARE_METATYPE(QVector<FakeTag>)
+Q_DECLARE_METATYPE(Akonadi::Protocol::ItemChangeNotification)
class AkAppendHandlerTest : public QObject
{
@@ -75,10 +76,10 @@ public:
pimItem.setSize(size);
}
- void updateNotifcationEntity(Protocol::ChangeNotification &ntf, const PimItem &pimItem)
+ void updateNotifcationEntity(Protocol::ItemChangeNotification &ntf, const PimItem &pimItem)
{
- ntf.clearEntities();
- ntf.addEntity(pimItem.id(), pimItem.remoteId(), pimItem.remoteRevision(), pimItem.mimeType().name());
+ ntf.clearItems();
+ ntf.addItem(pimItem.id(), pimItem.remoteId(), pimItem.remoteRevision(), pimItem.mimeType().name());
}
struct PartHelper
@@ -206,7 +207,7 @@ private Q_SLOTS:
void testAkAppend_data()
{
QTest::addColumn<TestScenario::List>("scenarios");
- QTest::addColumn<Protocol::ChangeNotification>("notification");
+ QTest::addColumn<Protocol::ItemChangeNotification>("notification");
QTest::addColumn<PimItem>("pimItem");
QTest::addColumn<QVector<FakePart> >("parts");
QTest::addColumn<QVector<Flag> >("flags");
@@ -216,7 +217,7 @@ private Q_SLOTS:
QTest::addColumn<bool>("expectFail");
TestScenario::List scenarios;
- Protocol::ChangeNotification notification;
+ Protocol::ItemChangeNotification notification;
qint64 uidnext = 0;
QDateTime datetime(QDate(2014, 05, 12), QTime(14, 46, 00), Qt::UTC);
PimItem pimItem;
@@ -232,11 +233,10 @@ private Q_SLOTS:
pimItem.setMimeType(MimeType::retrieveByName(QLatin1String("application/octet-stream")));
pimItem.setDatetime(datetime);
updateParts(parts, { { QLatin1String("PLD:DATA"), "0123456789", 10 } });
- notification.setType(Protocol::ChangeNotification::Items);
- notification.setOperation(Protocol::ChangeNotification::Add);
+ notification.setOperation(Protocol::ItemChangeNotification::Add);
notification.setParentCollection(4);
notification.setResource("akonadi_fake_resource_0");
- notification.addEntity(-1, QLatin1String("TEST-1"), QLatin1String("1"), QLatin1String("application/octet-stream"));
+ notification.addItem(-1, QLatin1String("TEST-1"), QLatin1String("1"), QLatin1String("application/octet-stream"));
notification.setSessionId(FakeAkonadiServer::instanceName().toLatin1());
uidnext = 13;
scenarios << FakeAkonadiServer::loginScenario()
@@ -284,7 +284,7 @@ private Q_SLOTS:
scenarios << FakeAkonadiServer::loginScenario()
<< inScenario
<< errorResponse(QLatin1String("Invalid parent collection"));
- QTest::newRow("invalid collection") << scenarios << Protocol::ChangeNotification()
+ QTest::newRow("invalid collection") << scenarios << Protocol::ItemChangeNotification()
<< PimItem() << QVector<FakePart>()
<< QVector<Flag>() << QVector<FakeTag>()
<< -1ll << QDateTime() << true;
@@ -298,7 +298,7 @@ private Q_SLOTS:
scenarios << FakeAkonadiServer::loginScenario()
<< inScenario
<< errorResponse(QLatin1String("Cannot append item into virtual collection"));
- QTest::newRow("virtual collection") << scenarios << Protocol::ChangeNotification()
+ QTest::newRow("virtual collection") << scenarios << Protocol::ItemChangeNotification()
<< PimItem() << QVector<FakePart>()
<< QVector<Flag>() << QVector<FakeTag>()
<< -1ll << QDateTime() << true;
@@ -346,7 +346,7 @@ private Q_SLOTS:
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::StreamPayloadCommand("PLD:DATA", Protocol::StreamPayloadCommand::Data))
<< TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponse("PLD:DATA", "123"))
<< errorResponse(QLatin1String("Payload size mismatch"));
- QTest::newRow("incomplete part data") << scenarios << Protocol::ChangeNotification()
+ QTest::newRow("incomplete part data") << scenarios << Protocol::ItemChangeNotification()
<< PimItem() << QVector<FakePart>()
<< QVector<Flag>() << QVector<FakeTag>()
<< -1ll << QDateTime() << true;
@@ -359,7 +359,7 @@ private Q_SLOTS:
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::StreamPayloadCommand("PLD:DATA", Protocol::StreamPayloadCommand::Data))
<< TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponse("PLD:DATA", "1234567890"))
<< errorResponse(QLatin1String("Payload size mismatch"));
- QTest::newRow("part data larger than advertised") << scenarios << Protocol::ChangeNotification()
+ QTest::newRow("part data larger than advertised") << scenarios << Protocol::ItemChangeNotification()
<< PimItem() << QVector<FakePart>()
<< QVector<Flag>() << QVector<FakeTag>()
<< -1ll << QDateTime() << true;
@@ -672,7 +672,7 @@ private Q_SLOTS:
void testAkAppend()
{
QFETCH(TestScenario::List, scenarios);
- QFETCH(Protocol::ChangeNotification, notification);
+ QFETCH(Protocol::ItemChangeNotification, notification);
QFETCH(PimItem, pimItem);
QFETCH(QVector<FakePart>, parts);
QFETCH(QVector<Flag>, flags);
@@ -689,10 +689,10 @@ private Q_SLOTS:
QCOMPARE(notificationSpy->count(), 1);
Protocol::ChangeNotification::List notifications = notificationSpy->at(0).first().value<Protocol::ChangeNotification::List>();
QCOMPARE(notifications.count(), 1);
- const Protocol::ChangeNotification itemNotification = notifications.at(0);
+ const Protocol::ItemChangeNotification itemNotification = notifications.at(0);
QVERIFY(AkTest::compareNotifications(itemNotification, notification, QFlag(AkTest::NtfAll & ~ AkTest::NtfEntities)));
- QCOMPARE(itemNotification.entities().count(), notification.entities().count());
+ QCOMPARE(itemNotification.items().count(), notification.items().count());
} else {
QVERIFY(notificationSpy->isEmpty());
}
diff --git a/autotests/server/collectionreferencetest.cpp b/autotests/server/collectionreferencetest.cpp
index 2ef16b6..9ae3185 100644
--- a/autotests/server/collectionreferencetest.cpp
+++ b/autotests/server/collectionreferencetest.cpp
@@ -35,7 +35,6 @@
using namespace Akonadi;
using namespace Akonadi::Server;
-Q_DECLARE_METATYPE(QList<Akonadi::Protocol::ChangeNotification>)
Q_DECLARE_METATYPE(Collection::List)
class CollectionReferenceTest : public QObject
@@ -71,12 +70,13 @@ private Q_SLOTS:
void testModify_data()
{
QTest::addColumn<TestScenario::List>("scenarios");
- QTest::addColumn<QList<Akonadi::Protocol::ChangeNotification> >("expectedNotifications");
+ QTest::addColumn<QList<Protocol::ChangeNotification> >("expectedNotifications");
- Akonadi::Protocol::ChangeNotification notificationTemplate;
- notificationTemplate.setType(Protocol::ChangeNotification::Collections);
- notificationTemplate.setOperation(Protocol::ChangeNotification::Modify);
- notificationTemplate.addEntity(initializer.collection("col2").id(), QLatin1String("col2"), QLatin1String(""));
+ Protocol::CollectionChangeNotification notificationTemplate;
+ notificationTemplate.setOperation(Protocol::CollectionChangeNotification::Modify);
+ notificationTemplate.setId(initializer.collection("col2").id());
+ notificationTemplate.setRemoteId(QStringLiteral("col2"));
+ notificationTemplate.setRemoteRevision(QStringLiteral(""));
notificationTemplate.setParentCollection(0);
notificationTemplate.setResource("testresource");
notificationTemplate.setSessionId(FakeAkonadiServer::instanceName().toLatin1());
@@ -91,7 +91,7 @@ private Q_SLOTS:
<< TestScenario::create(5, TestScenario::ClientCmd, cmd)
<< TestScenario::create(5, TestScenario::ServerCmd, initializer.listResponse(initializer.collection("col1")))
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::FetchCollectionsResponse());
- QTest::newRow("list before referenced first level") << scenarios << QList<Akonadi::Protocol::ChangeNotification>();
+ QTest::newRow("list before referenced first level") << scenarios << QList<Protocol::ChangeNotification>();
}
{
@@ -103,10 +103,10 @@ private Q_SLOTS:
<< TestScenario::create(5, TestScenario::ClientCmd, cmd)
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyCollectionResponse());
- Akonadi::Protocol::ChangeNotification notification = notificationTemplate;
- notification.setItemParts(QSet<QByteArray>() << "REFERENCED");
+ Protocol::CollectionChangeNotification notification = notificationTemplate;
+ notification.setChangedParts(QSet<QByteArray>() << "REFERENCED");
- QTest::newRow("reference") << scenarios << (QList<Akonadi::Protocol::ChangeNotification>() << notification);
+ QTest::newRow("reference") << scenarios << (QList<Protocol::ChangeNotification>() << notification);
}
{
@@ -127,10 +127,10 @@ private Q_SLOTS:
<< TestScenario::create(6, TestScenario::ServerCmd, initializer.listResponse(col2))
<< TestScenario::create(6, TestScenario::ServerCmd, Protocol::FetchCollectionsResponse());
- Akonadi::Protocol::ChangeNotification notification = notificationTemplate;
- notification.setItemParts(QSet<QByteArray>() << "REFERENCED");
+ Protocol::CollectionChangeNotification notification = notificationTemplate;
+ notification.setChangedParts(QSet<QByteArray>() << "REFERENCED");
- QTest::newRow("list referenced base") << scenarios << (QList<Akonadi::Protocol::ChangeNotification>() << notification);
+ QTest::newRow("list referenced base") << scenarios << (QList<Protocol::ChangeNotification>() << notification);
}
{
Protocol::ModifyCollectionCommand cmd(initializer.collection("col2").id());
@@ -152,10 +152,10 @@ private Q_SLOTS:
<< TestScenario::create(6, TestScenario::ServerCmd, initializer.listResponse(col2))
<< TestScenario::create(6, TestScenario::ServerCmd, Protocol::FetchCollectionsResponse());
- Akonadi::Protocol::ChangeNotification notification = notificationTemplate;
- notification.setItemParts(QSet<QByteArray>() << "REFERENCED");
+ Protocol::CollectionChangeNotification notification = notificationTemplate;
+ notification.setChangedParts(QSet<QByteArray>() << "REFERENCED");
- QTest::newRow("list referenced first level") << scenarios << (QList<Akonadi::Protocol::ChangeNotification>() << notification);
+ QTest::newRow("list referenced first level") << scenarios << (QList<Protocol::ChangeNotification>() << notification);
}
{
Protocol::ModifyCollectionCommand cmd1(initializer.collection("col2").id());
@@ -171,10 +171,10 @@ private Q_SLOTS:
<< TestScenario::create(6, TestScenario::ClientCmd, cmd2)
<< TestScenario::create(6, TestScenario::ServerCmd, Protocol::ModifyCollectionResponse());
- Akonadi::Protocol::ChangeNotification notification = notificationTemplate;
- notification.setItemParts(QSet<QByteArray>() << "REFERENCED");
+ Protocol::CollectionChangeNotification notification = notificationTemplate;
+ notification.setChangedParts(QSet<QByteArray>() << "REFERENCED");
- QTest::newRow("dereference") << scenarios << (QList<Akonadi::Protocol::ChangeNotification>() << notification << notification);
+ QTest::newRow("dereference") << scenarios << (QList<Protocol::ChangeNotification>() << notification << notification);
}
}
diff --git a/autotests/server/createhandlertest.cpp b/autotests/server/createhandlertest.cpp
index 9bb2d35..ccd8cc6 100644
--- a/autotests/server/createhandlertest.cpp
+++ b/autotests/server/createhandlertest.cpp
@@ -33,6 +33,8 @@
using namespace Akonadi;
using namespace Akonadi::Server;
+Q_DECLARE_METATYPE(Akonadi::Protocol::CollectionChangeNotification)
+
class CreateHandlerTest : public QObject
{
Q_OBJECT
@@ -59,11 +61,10 @@ private Q_SLOTS:
DbInitializer dbInitializer;
QTest::addColumn<TestScenario::List >("scenarios");
- QTest::addColumn<Akonadi::Protocol::ChangeNotification>("notification");
+ QTest::addColumn<Akonadi::Protocol::CollectionChangeNotification>("notification");
- Akonadi::Protocol::ChangeNotification notificationTemplate;
- notificationTemplate.setType(Protocol::ChangeNotification::Collections);
- notificationTemplate.setOperation(Protocol::ChangeNotification::Add);
+ Akonadi::Protocol::CollectionChangeNotification notificationTemplate;
+ notificationTemplate.setOperation(Protocol::CollectionChangeNotification::Add);
notificationTemplate.setParentCollection(3);
notificationTemplate.setResource("akonadi_fake_resource_0");
notificationTemplate.setSessionId(FakeAkonadiServer::instanceName().toLatin1());
@@ -89,8 +90,10 @@ private Q_SLOTS:
<< TestScenario::create(5, TestScenario::ServerCmd, resp)
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::CreateCollectionResponse());
- Akonadi::Protocol::ChangeNotification notification = notificationTemplate;
- notification.addEntity(8, QLatin1String(""), QLatin1String(""));
+ Akonadi::Protocol::CollectionChangeNotification notification = notificationTemplate;
+ notification.setId(8);
+ notification.setRemoteId(QStringLiteral(""));
+ notification.setRemoteRevision(QStringLiteral(""));
QTest::newRow("create collection") << scenarios << notification;
}
@@ -122,8 +125,10 @@ private Q_SLOTS:
<< TestScenario::create(5, TestScenario::ServerCmd, resp)
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::CreateCollectionResponse());
- Akonadi::Protocol::ChangeNotification notification = notificationTemplate;
- notification.addEntity(9, QLatin1String(""), QLatin1String(""));
+ Akonadi::Protocol::CollectionChangeNotification notification = notificationTemplate;
+ notification.setId(9);
+ notification.setRemoteId(QStringLiteral(""));
+ notification.setRemoteRevision(QStringLiteral(""));
QTest::newRow("create collection with local override") << scenarios << notification;
}
@@ -149,10 +154,12 @@ private Q_SLOTS:
<< TestScenario::create(5, TestScenario::ServerCmd, resp)
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::CreateCollectionResponse());
- Akonadi::Protocol::ChangeNotification notification = notificationTemplate;
+ Akonadi::Protocol::CollectionChangeNotification notification = notificationTemplate;
notification.setSessionId("akonadi_fake_resource_0");
notification.setParentCollection(0);
- notification.addEntity(10, QLatin1String(""), QLatin1String(""));
+ notification.setId(10);
+ notification.setRemoteId(QStringLiteral(""));
+ notification.setRemoteRevision(QStringLiteral(""));
QTest::newRow("create top-level collection") << scenarios << notification;
}
@@ -162,7 +169,7 @@ private Q_SLOTS:
void testCreate()
{
QFETCH(TestScenario::List, scenarios);
- QFETCH(Protocol::ChangeNotification, notification);
+ QFETCH(Protocol::CollectionChangeNotification, notification);
FakeAkonadiServer::instance()->setScenarios(scenarios);
FakeAkonadiServer::instance()->runTest();
@@ -172,7 +179,7 @@ private Q_SLOTS:
QCOMPARE(notificationSpy->count(), 1);
const Protocol::ChangeNotification::List notifications = notificationSpy->takeFirst().first().value<Protocol::ChangeNotification::List>();
QCOMPARE(notifications.count(), 1);
- QCOMPARE(notifications.first(), notification);
+ QCOMPARE(Protocol::CollectionChangeNotification(notifications.first()), notification);
} else {
QVERIFY(notificationSpy->isEmpty() || notificationSpy->takeFirst().first().value<Protocol::ChangeNotification::List>().isEmpty());
}
diff --git a/autotests/server/fakeakonadiserver.cpp b/autotests/server/fakeakonadiserver.cpp
index b3f5375..9ac4f8b 100644
--- a/autotests/server/fakeakonadiserver.cpp
+++ b/autotests/server/fakeakonadiserver.cpp
@@ -37,6 +37,7 @@
#include <private/standarddirs_p.h>
#include <shared/akapplication.h>
+#include "aklocalserver.h"
#include "storage/dbconfig.h"
#include "storage/datastore.h"
#include "preprocessormanager.h"
@@ -101,9 +102,10 @@ FakeAkonadiServer *FakeAkonadiServer::instance()
FakeAkonadiServer::FakeAkonadiServer()
: AkonadiServer()
- , mDataStore(0)
- , mServerLoop(0)
- , mNotificationSpy(0)
+ , mDataStore(Q_NULLPTR)
+ , mServerLoop(Q_NULLPTR)
+ , mNtfCollector(Q_NULLPTR)
+ , mNotificationSpy(Q_NULLPTR)
, mPopulateDb(true)
{
qputenv("AKONADI_INSTANCE", qPrintable(instanceName()));
@@ -120,6 +122,7 @@ FakeAkonadiServer::~FakeAkonadiServer()
delete mClient;
delete mConnection;
delete mNotificationSpy;
+ delete mNtfCollector;
}
QString FakeAkonadiServer::basePath()
@@ -129,7 +132,7 @@ QString FakeAkonadiServer::basePath()
QString FakeAkonadiServer::socketFile()
{
- return basePath() % QLatin1String("/local/share/akonadi/akonadiserver.socket");
+ return basePath() % QStringLiteral("/local/share/akonadi/akonadiserver.socket");
}
QString FakeAkonadiServer::instanceName()
@@ -249,7 +252,9 @@ bool FakeAkonadiServer::quit()
qDebug() << "==== Fake Akonadi Server shutting down ====";
// Stop listening for connections
- close();
+ if (mCmdServer) {
+ mCmdServer->close();
+ }
const QCommandLineParser &args = AkApplicationBase::instance()->commandLineArguments();
if (!args.isSet(QLatin1String("no-cleanup"))) {
@@ -275,23 +280,27 @@ void FakeAkonadiServer::setScenarios(const TestScenario::List &scenarios)
mClient->setScenarios(scenarios);
}
-void FakeAkonadiServer::incomingConnection(quintptr socketDescriptor)
+void FakeAkonadiServer::newCmdConnection(quintptr socketDescriptor)
{
mConnection = new FakeConnection(socketDescriptor);
- NotificationCollector *ntfCollector = Q_NULLPTR;
+ delete mNotificationSpy;
+ delete mNtfCollector;
+
// Connection is it's own thread, so we have to make sure we get collector
// from DataStore of the Connection's thread, not ours
QMetaObject::invokeMethod(mConnection, "notificationCollector", Qt::BlockingQueuedConnection,
- Q_RETURN_ARG(Akonadi::Server::NotificationCollector*, ntfCollector));
- Q_ASSERT(ntfCollector);
- mNotificationSpy = new QSignalSpy(ntfCollector,
- SIGNAL(notify(Akonadi::Protocol::ChangeNotification::List)));
+ Q_RETURN_ARG(Akonadi::Server::NotificationCollector*, mNtfCollector));
+ Q_ASSERT(mNtfCollector);
+ mNotificationSpy = new QSignalSpy(mNtfCollector, &Server::NotificationCollector::notify);
Q_ASSERT(mNotificationSpy->isValid());
}
void FakeAkonadiServer::runTest()
{
- QVERIFY(listen(socketFile()));
+ mCmdServer = new AkLocalServer(this);
+ connect(mCmdServer, static_cast<void(AkLocalServer::*)(quintptr)>(&AkLocalServer::newConnection),
+ this, &FakeAkonadiServer::newCmdConnection);
+ QVERIFY(mCmdServer->listen(socketFile()));
mServerLoop = new QEventLoop(this);
connect(mClient, &QThread::finished, mServerLoop, &QEventLoop::quit);
@@ -306,7 +315,10 @@ void FakeAkonadiServer::runTest()
mServerLoop->deleteLater();
mServerLoop = 0;
- close();
+ mCmdServer->close();
+
+ // Flush any pending notifications
+ mNtfCollector->dispatchNotifications();
}
QSignalSpy *FakeAkonadiServer::notificationSpy() const
diff --git a/autotests/server/fakeakonadiserver.h b/autotests/server/fakeakonadiserver.h
index b0b917c..582cd89 100644
--- a/autotests/server/fakeakonadiserver.h
+++ b/autotests/server/fakeakonadiserver.h
@@ -110,8 +110,7 @@ public:
void setPopulateDb(bool populate);
protected:
- /* Reimpl */
- void incomingConnection(quintptr socketDescriptor) Q_DECL_OVERRIDE;
+ void newCmdConnection(quintptr socketDescriptor) Q_DECL_OVERRIDE;
private:
explicit FakeAkonadiServer();
@@ -125,6 +124,7 @@ private:
QEventLoop *mServerLoop;
+ NotificationCollector *mNtfCollector;
QSignalSpy *mNotificationSpy;
bool mPopulateDb;
diff --git a/autotests/server/linkhandlertest.cpp b/autotests/server/linkhandlertest.cpp
index 28644ee..1d24433 100644
--- a/autotests/server/linkhandlertest.cpp
+++ b/autotests/server/linkhandlertest.cpp
@@ -34,6 +34,8 @@
using namespace Akonadi;
using namespace Akonadi::Server;
+Q_DECLARE_METATYPE(Akonadi::Protocol::ItemChangeNotification)
+
class LinkHandlerTest : public QObject
{
Q_OBJECT
@@ -65,22 +67,21 @@ private Q_SLOTS:
void testLink_data()
{
QTest::addColumn<TestScenario::List>("scenarios");
- QTest::addColumn<Akonadi::Protocol::ChangeNotification>("notification");
+ QTest::addColumn<Akonadi::Protocol::ItemChangeNotification>("notification");
QTest::addColumn<bool>("expectFail");
TestScenario::List scenarios;
- Protocol::ChangeNotification notification;
+ Protocol::ItemChangeNotification notification;
scenarios << FakeAkonadiServer::loginScenario()
<< TestScenario::create(5, TestScenario::ClientCmd, Protocol::LinkItemsCommand(Protocol::LinkItemsCommand::Link, ImapInterval(1, 3), 3))
<< TestScenario::create(5, TestScenario::ServerCmd, createError(QLatin1String("Can't link items to non-virtual collections")));
- QTest::newRow("non-virtual collection") << scenarios << Protocol::ChangeNotification() << true;
+ QTest::newRow("non-virtual collection") << scenarios << Protocol::ItemChangeNotification() << true;
- notification.setType(Protocol::ChangeNotification::Items);
- notification.setOperation(Protocol::ChangeNotification::Link);
- notification.addEntity(1, QLatin1String("A"), QString(), QLatin1String("application/octet-stream"));
- notification.addEntity(2, QLatin1String("B"), QString(), QLatin1String("application/octet-stream"));
- notification.addEntity(3, QLatin1String("C"), QString(), QLatin1String("application/octet-stream"));
+ notification.setOperation(Protocol::ItemChangeNotification::Link);
+ notification.addItem(1, QLatin1String("A"), QString(), QLatin1String("application/octet-stream"));
+ notification.addItem(2, QLatin1String("B"), QString(), QLatin1String("application/octet-stream"));
+ notification.addItem(3, QLatin1String("C"), QString(), QLatin1String("application/octet-stream"));
notification.setParentCollection(6);
notification.setResource("akonadi_fake_resource_with_virtual_collections_0");
notification.setSessionId(FakeAkonadiServer::instanceName().toLatin1());
@@ -91,8 +92,8 @@ private Q_SLOTS:
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::LinkItemsResponse());
QTest::newRow("normal") << scenarios << notification << false;
- notification.clearEntities();
- notification.addEntity(4, QLatin1String("D"), QString(), QLatin1String("application/octet-stream"));
+ notification.clearItems();
+ notification.addItem(4, QLatin1String("D"), QString(), QLatin1String("application/octet-stream"));
scenarios.clear();
scenarios << FakeAkonadiServer::loginScenario()
<< TestScenario::create(5, TestScenario::ClientCmd, Protocol::LinkItemsCommand(Protocol::LinkItemsCommand::Link, QVector<qint64>{ 4, 123456 }, 6))
@@ -103,7 +104,7 @@ private Q_SLOTS:
scenarios << FakeAkonadiServer::loginScenario()
<< TestScenario::create(5, TestScenario::ClientCmd, Protocol::LinkItemsCommand(Protocol::LinkItemsCommand::Link, 4, 6))
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::LinkItemsResponse());
- QTest::newRow("non-existent item only") << scenarios << Protocol::ChangeNotification() << false;
+ QTest::newRow("non-existent item only") << scenarios << Protocol::ItemChangeNotification() << false;
//FIXME: All RID related operations are currently broken because we reset the collection context before every command,
//and LINK still relies on SELECT to set the collection context.
@@ -143,7 +144,7 @@ private Q_SLOTS:
void testLink()
{
QFETCH(TestScenario::List, scenarios);
- QFETCH(Protocol::ChangeNotification, notification);
+ QFETCH(Protocol::ItemChangeNotification, notification);
QFETCH(bool, expectFail);
FakeAkonadiServer::instance()->setScenarios(scenarios);
@@ -154,12 +155,12 @@ private Q_SLOTS:
QCOMPARE(notificationSpy->count(), 1);
const Protocol::ChangeNotification::List notifications = notificationSpy->takeFirst().first().value<Protocol::ChangeNotification::List>();
QCOMPARE(notifications.count(), 1);
- QCOMPARE(notifications.first(), notification);
+ QCOMPARE(Protocol::ItemChangeNotification(notifications.first()), notification);
} else {
QVERIFY(notificationSpy->isEmpty() || notificationSpy->takeFirst().first().value<Protocol::ChangeNotification::List>().isEmpty());
}
- Q_FOREACH (const Protocol::ChangeNotification::Entity &entity, notification.entities()) {
+ Q_FOREACH (const auto &entity, notification.items()) {
if (expectFail) {
QVERIFY(!Collection::relatesToPimItem(notification.parentCollection(), entity.id));
} else {
@@ -171,22 +172,21 @@ private Q_SLOTS:
void testUnlink_data()
{
QTest::addColumn<TestScenario::List>("scenarios");
- QTest::addColumn<Akonadi::Protocol::ChangeNotification>("notification");
+ QTest::addColumn<Akonadi::Protocol::ItemChangeNotification>("notification");
QTest::addColumn<bool>("expectFail");
TestScenario::List scenarios;
- Protocol::ChangeNotification notification;
+ Protocol::ItemChangeNotification notification;
scenarios << FakeAkonadiServer::loginScenario()
<< TestScenario::create(5, TestScenario::ClientCmd, Protocol::LinkItemsCommand(Protocol::LinkItemsCommand::Unlink, ImapInterval(1, 3), 3))
<< TestScenario::create(5, TestScenario::ServerCmd, createError(QLatin1String("Can't link items to non-virtual collections")));
- QTest::newRow("non-virtual collection") << scenarios << Protocol::ChangeNotification() << true;
+ QTest::newRow("non-virtual collection") << scenarios << Protocol::ItemChangeNotification() << true;
- notification.setType(Protocol::ChangeNotification::Items);
- notification.setOperation(Protocol::ChangeNotification::Unlink);
- notification.addEntity(1, QLatin1String("A"), QString(), QLatin1String("application/octet-stream"));
- notification.addEntity(2, QLatin1String("B"), QString(), QLatin1String("application/octet-stream"));
- notification.addEntity(3, QLatin1String("C"), QString(), QLatin1String("application/octet-stream"));
+ notification.setOperation(Protocol::ItemChangeNotification::Unlink);
+ notification.addItem(1, QLatin1String("A"), QString(), QLatin1String("application/octet-stream"));
+ notification.addItem(2, QLatin1String("B"), QString(), QLatin1String("application/octet-stream"));
+ notification.addItem(3, QLatin1String("C"), QString(), QLatin1String("application/octet-stream"));
notification.setParentCollection(6);
notification.setResource("akonadi_fake_resource_with_virtual_collections_0");
notification.setSessionId(FakeAkonadiServer::instanceName().toLatin1());
@@ -196,8 +196,8 @@ private Q_SLOTS:
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::LinkItemsResponse());
QTest::newRow("normal") << scenarios << notification << false;
- notification.clearEntities();
- notification.addEntity(4, QLatin1String("D"), QString(), QLatin1String("application/octet-stream"));
+ notification.clearItems();
+ notification.addItem(4, QLatin1String("D"), QString(), QLatin1String("application/octet-stream"));
scenarios.clear();
scenarios << FakeAkonadiServer::loginScenario()
<< TestScenario::create(5, TestScenario::ClientCmd, Protocol::LinkItemsCommand(Protocol::LinkItemsCommand::Unlink, QVector<qint64>{ 4, 2048 }, 6))
@@ -208,7 +208,7 @@ private Q_SLOTS:
scenarios << FakeAkonadiServer::loginScenario()
<< TestScenario::create(5, TestScenario::ClientCmd, Protocol::LinkItemsCommand(Protocol::LinkItemsCommand::Unlink, 4096, 6))
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::LinkItemsResponse());
- QTest::newRow("non-existent item only") << scenarios << Protocol::ChangeNotification() << false;
+ QTest::newRow("non-existent item only") << scenarios << Protocol::ItemChangeNotification() << false;
//FIXME: All RID related operations are currently broken because we reset the collection context before every command,
//and LINK still relies on SELECT to set the collection context.
@@ -248,7 +248,7 @@ private Q_SLOTS:
void testUnlink()
{
QFETCH(TestScenario::List, scenarios);
- QFETCH(Protocol::ChangeNotification, notification);
+ QFETCH(Protocol::ItemChangeNotification, notification);
QFETCH(bool, expectFail);
FakeAkonadiServer::instance()->setScenarios(scenarios);
@@ -259,12 +259,12 @@ private Q_SLOTS:
QCOMPARE(notificationSpy->count(), 1);
const Protocol::ChangeNotification::List notifications = notificationSpy->takeFirst().first().value<Protocol::ChangeNotification::List>();
QCOMPARE(notifications.count(), 1);
- QCOMPARE(notifications.first(), notification);
+ QCOMPARE(Protocol::ItemChangeNotification(notifications.first()), notification);
} else {
QVERIFY(notificationSpy->isEmpty() || notificationSpy->takeFirst().first().value<Protocol::ChangeNotification::List>().isEmpty());
}
- Q_FOREACH (const Protocol::ChangeNotification::Entity &entity, notification.entities()) {
+ Q_FOREACH (const auto &entity, notification.items()) {
if (expectFail) {
QVERIFY(Collection::relatesToPimItem(notification.parentCollection(), entity.id));
} else {
diff --git a/autotests/server/modifyhandlertest.cpp b/autotests/server/modifyhandlertest.cpp
index 866be4c..d44a44d 100644
--- a/autotests/server/modifyhandlertest.cpp
+++ b/autotests/server/modifyhandlertest.cpp
@@ -33,7 +33,7 @@
using namespace Akonadi;
using namespace Akonadi::Server;
-Q_DECLARE_METATYPE(QList<Akonadi::Protocol::ChangeNotification>)
+Q_DECLARE_METATYPE(Akonadi::Protocol::CollectionChangeNotification::List)
class ModifyHandlerTest : public QObject
{
@@ -59,13 +59,14 @@ private Q_SLOTS:
void testModify_data()
{
QTest::addColumn<TestScenario::List>("scenarios");
- QTest::addColumn<QList<Akonadi::Protocol::ChangeNotification> >("expectedNotifications");
+ QTest::addColumn<Protocol::CollectionChangeNotification::List>("expectedNotifications");
QTest::addColumn<QVariant>("newValue");
- Akonadi::Protocol::ChangeNotification notificationTemplate;
- notificationTemplate.setType(Protocol::ChangeNotification::Collections);
- notificationTemplate.setOperation(Protocol::ChangeNotification::Modify);
- notificationTemplate.addEntity(5, QStringLiteral("ColD"), QStringLiteral(""));
+ Protocol::CollectionChangeNotification notificationTemplate;
+ notificationTemplate.setOperation(Protocol::CollectionChangeNotification::Modify);
+ notificationTemplate.setId(5);
+ notificationTemplate.setRemoteId(QStringLiteral("ColD"));
+ notificationTemplate.setRemoteRevision(QStringLiteral(""));
notificationTemplate.setParentCollection(4);
notificationTemplate.setResource("akonadi_fake_resource_0");
notificationTemplate.setSessionId(FakeAkonadiServer::instanceName().toLatin1());
@@ -79,10 +80,10 @@ private Q_SLOTS:
<< TestScenario::create(5, TestScenario::ClientCmd, cmd)
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyCollectionResponse());
- Akonadi::Protocol::ChangeNotification notification = notificationTemplate;
- notification.setItemParts(QSet<QByteArray>() << "NAME");
+ Protocol::CollectionChangeNotification notification = notificationTemplate;
+ notification.setChangedParts(QSet<QByteArray>() << "NAME");
- QTest::newRow("modify collection") << scenarios << (QList<Akonadi::Protocol::ChangeNotification>() << notification) << QVariant::fromValue(QString::fromLatin1("New Name"));
+ QTest::newRow("modify collection") << scenarios << Protocol::CollectionChangeNotification::List{ notification } << QVariant::fromValue(QString::fromLatin1("New Name"));
}
{
Protocol::ModifyCollectionCommand cmd(5);
@@ -93,12 +94,12 @@ private Q_SLOTS:
<< TestScenario::create(5, TestScenario::ClientCmd, cmd)
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyCollectionResponse());
- Akonadi::Protocol::ChangeNotification notification = notificationTemplate;
- notification.setItemParts(QSet<QByteArray>() << "ENABLED");
- Akonadi::Protocol::ChangeNotification unsubscribeNotification = notificationTemplate;
- unsubscribeNotification.setOperation(Protocol::ChangeNotification::Unsubscribe);
+ Protocol::CollectionChangeNotification notification = notificationTemplate;
+ notification.setChangedParts(QSet<QByteArray>() << "ENABLED");
+ Protocol::CollectionChangeNotification unsubscribeNotification = notificationTemplate;
+ unsubscribeNotification.setOperation(Protocol::CollectionChangeNotification::Unsubscribe);
- QTest::newRow("disable collection") << scenarios << (QList<Akonadi::Protocol::ChangeNotification>() << notification << unsubscribeNotification) << QVariant::fromValue(false);
+ QTest::newRow("disable collection") << scenarios << Protocol::CollectionChangeNotification::List{ notification, unsubscribeNotification} << QVariant::fromValue(false);
}
{
Protocol::ModifyCollectionCommand cmd(5);
@@ -109,12 +110,12 @@ private Q_SLOTS:
<< TestScenario::create(5, TestScenario::ClientCmd, cmd)
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyCollectionResponse());
- Akonadi::Protocol::ChangeNotification notification = notificationTemplate;
- notification.setItemParts(QSet<QByteArray>() << "ENABLED");
- Akonadi::Protocol::ChangeNotification subscribeNotification = notificationTemplate;
- subscribeNotification.setOperation(Protocol::ChangeNotification::Subscribe);
+ Protocol::CollectionChangeNotification notification = notificationTemplate;
+ notification.setChangedParts(QSet<QByteArray>() << "ENABLED");
+ Protocol::CollectionChangeNotification subscribeNotification = notificationTemplate;
+ subscribeNotification.setOperation(Protocol::CollectionChangeNotification::Subscribe);
- QTest::newRow("enable collection") << scenarios << (QList<Akonadi::Protocol::ChangeNotification>() << notification << subscribeNotification) << QVariant::fromValue(true);
+ QTest::newRow("enable collection") << scenarios << Protocol::CollectionChangeNotification::List{ notification, subscribeNotification } << QVariant::fromValue(true);
}
{
Protocol::ModifyCollectionCommand cmd(5);
@@ -128,19 +129,19 @@ private Q_SLOTS:
<< TestScenario::create(5, TestScenario::ClientCmd, cmd)
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyCollectionResponse());
- Akonadi::Protocol::ChangeNotification notification = notificationTemplate;
- notification.setItemParts(QSet<QByteArray>() << "ENABLED" << "SYNC" << "DISPLAY" << "INDEX");
- Akonadi::Protocol::ChangeNotification unsubscribeNotification = notificationTemplate;
- unsubscribeNotification.setOperation(Protocol::ChangeNotification::Unsubscribe);
+ Protocol::CollectionChangeNotification notification = notificationTemplate;
+ notification.setChangedParts(QSet<QByteArray>() << "ENABLED" << "SYNC" << "DISPLAY" << "INDEX");
+ Protocol::CollectionChangeNotification unsubscribeNotification = notificationTemplate;
+ unsubscribeNotification.setOperation(Protocol::CollectionChangeNotification::Unsubscribe);
- QTest::newRow("local override enable") << scenarios << (QList<Akonadi::Protocol::ChangeNotification>() << notification << unsubscribeNotification) << QVariant::fromValue(true);
+ QTest::newRow("local override enable") << scenarios << Protocol::CollectionChangeNotification::List{ notification, unsubscribeNotification } << QVariant::fromValue(true);
}
}
void testModify()
{
QFETCH(TestScenario::List, scenarios);
- QFETCH(QList<Protocol::ChangeNotification>, expectedNotifications);
+ QFETCH(Protocol::CollectionChangeNotification::List, expectedNotifications);
QFETCH(QVariant, newValue);
FakeAkonadiServer::instance()->setScenarios(scenarios);
@@ -158,22 +159,20 @@ private Q_SLOTS:
QCOMPARE(receivedNotifications.size(), expectedNotifications.count());
for (int i = 0; i < expectedNotifications.size(); i++) {
- QCOMPARE(receivedNotifications.at(i), expectedNotifications.at(i));
- Protocol::ChangeNotification notification = receivedNotifications.at(i);
- Q_FOREACH (const Protocol::ChangeNotification::Entity &entity, notification.entities()) {
- if (notification.itemParts().contains("NAME")) {
- Collection col = Collection::retrieveById(entity.id);
- QCOMPARE(col.name(), newValue.toString());
- }
- if (notification.itemParts().contains("ENABLED") || notification.itemParts().contains("SYNC") || notification.itemParts().contains("DISPLAY") || notification.itemParts().contains("INDEX")) {
- Collection col = Collection::retrieveById(entity.id);
- const bool sync = col.syncPref() == Tristate::Undefined ? col.enabled() : col.syncPref() == Tristate::True;
- QCOMPARE(sync, newValue.toBool());
- const bool display = col.displayPref() == Tristate::Undefined ? col.enabled() : col.displayPref() == Tristate::True;
- QCOMPARE(display, newValue.toBool());
- const bool index = col.indexPref() == Tristate::Undefined ? col.enabled() : col.indexPref() == Tristate::True;
- QCOMPARE(index, newValue.toBool());
- }
+ QCOMPARE(Protocol::CollectionChangeNotification(receivedNotifications.at(i)), expectedNotifications.at(i));
+ Protocol::CollectionChangeNotification notification = receivedNotifications.at(i);
+ if (notification.changedParts().contains("NAME")) {
+ Collection col = Collection::retrieveById(notification.id());
+ QCOMPARE(col.name(), newValue.toString());
+ }
+ if (!notification.changedParts().intersect({ "ENABLED", "SYNC", "DISPLAY", "INDEX" }).isEmpty()) {
+ Collection col = Collection::retrieveById(notification.id());
+ const bool sync = col.syncPref() == Tristate::Undefined ? col.enabled() : col.syncPref() == Tristate::True;
+ QCOMPARE(sync, newValue.toBool());
+ const bool display = col.displayPref() == Tristate::Undefined ? col.enabled() : col.displayPref() == Tristate::True;
+ QCOMPARE(display, newValue.toBool());
+ const bool index = col.indexPref() == Tristate::Undefined ? col.enabled() : col.indexPref() == Tristate::True;
+ QCOMPARE(index, newValue.toBool());
}
}
}
diff --git a/autotests/server/notificationmanagertest.cpp b/autotests/server/notificationmanagertest.cpp
index 7fed294..1b66c05 100644
--- a/autotests/server/notificationmanagertest.cpp
+++ b/autotests/server/notificationmanagertest.cpp
@@ -21,7 +21,7 @@
#include "entities.h"
#include "notificationmanager.h"
-#include "notificationsource.h"
+#include "notificationsubscriber.h"
#include <QtCore/QObject>
#include <QtTest/QTest>
@@ -33,11 +33,78 @@ using namespace Akonadi::Server;
Q_DECLARE_METATYPE(QVector<QString>)
+class TestableNotificationSubscriber : public NotificationSubscriber
+{
+public:
+ TestableNotificationSubscriber()
+ : NotificationSubscriber()
+ {
+ mSubscriber = "TestSubscriber";
+ }
+
+ void setAllMonitored(bool allMonitored)
+ {
+ mAllMonitored = allMonitored;
+ }
+
+ void setMonitoredCollection(qint64 collection, bool monitored)
+ {
+ if (monitored) {
+ mMonitoredCollections.insert(collection);
+ } else {
+ mMonitoredCollections.remove(collection);
+ }
+ }
+
+ void setMonitoredItem(qint64 item, bool monitored)
+ {
+ if (monitored) {
+ mMonitoredItems.insert(item);
+ } else {
+ mMonitoredItems.remove(item);
+ }
+ }
+
+ void setMonitoredResource(const QByteArray &resource, bool monitored)
+ {
+ if (monitored) {
+ mMonitoredResources.insert(resource);
+ } else {
+ mMonitoredResources.remove(resource);
+ }
+ }
+
+ void setMonitoredMimeType(const QString &mimeType, bool monitored)
+ {
+ if (monitored) {
+ mMonitoredMimeTypes.insert(mimeType);
+ } else {
+ mMonitoredMimeTypes.remove(mimeType);
+ }
+ }
+
+ void setIgnoredSession(const QByteArray &session, bool ignored)
+ {
+ if (ignored) {
+ mIgnoredSessions.insert(session);
+ } else {
+ mIgnoredSessions.remove(session);
+ }
+ }
+
+ void writeNotification(const Protocol::ChangeNotification &notification) Q_DECL_OVERRIDE
+ {
+ emittedNotifications << notification;
+ }
+
+ Protocol::ChangeNotification::List emittedNotifications;
+};
+
class NotificationManagerTest : public QObject
{
Q_OBJECT
- typedef QList<NotificationSource *> NSList;
+ typedef QList<NotificationSubscriber *> NSList;
private Q_SLOTS:
void testSourceFilter_data()
@@ -53,15 +120,14 @@ private Q_SLOTS:
QTest::addColumn<Protocol::ChangeNotification>("notification");
QTest::addColumn<bool>("accepted");
- Protocol::ChangeNotification msg;
+ Protocol::ItemChangeNotification itemMsg;
#define EmptyList(T) (QVector<T>())
#define List(T,x) (QVector<T>() << x)
- msg = Protocol::ChangeNotification();
- msg.setType(Protocol::ChangeNotification::Items);
- msg.setOperation(Protocol::ChangeNotification::Add);
- msg.setParentCollection(1);
+ itemMsg = Protocol::ItemChangeNotification();
+ itemMsg.setOperation(Protocol::ItemChangeNotification::Add);
+ itemMsg.setParentCollection(1);
QTest::newRow("monitorAll vs notification without items")
<< true
<< EmptyList(Entity::Id)
@@ -69,10 +135,10 @@ private Q_SLOTS:
<< EmptyList(QByteArray)
<< EmptyList(QString)
<< EmptyList(QByteArray)
- << msg
+ << Protocol::ChangeNotification(itemMsg)
<< false;
- msg.addEntity(1, QString(), QString(), QStringLiteral("message/rfc822"));
+ itemMsg.addItem(1, QString(), QString(), QStringLiteral("message/rfc822"));
QTest::newRow("monitorAll vs notification with one item")
<< true
<< EmptyList(Entity::Id)
@@ -80,7 +146,7 @@ private Q_SLOTS:
<< EmptyList(QByteArray)
<< EmptyList(QString)
<< EmptyList(QByteArray)
- << msg
+ << Protocol::ChangeNotification(itemMsg)
<< true;
QTest::newRow("item monitored but different mimetype")
@@ -90,7 +156,7 @@ private Q_SLOTS:
<< EmptyList(QByteArray)
<< List(QString, QStringLiteral("random/mimetype"))
<< EmptyList(QByteArray)
- << msg
+ << Protocol::ChangeNotification(itemMsg)
<< false;
QTest::newRow("item not monitored, but mimetype matches")
@@ -100,10 +166,10 @@ private Q_SLOTS:
<< EmptyList(QByteArray)
<< List(QString, QStringLiteral("message/rfc822"))
<< EmptyList(QByteArray)
- << msg
+ << Protocol::ChangeNotification(itemMsg)
<< true;
- msg.setSessionId("testSession");
+ itemMsg.setSessionId("testSession");
QTest::newRow("item monitored but session ignored")
<< false
<< EmptyList(Entity::Id)
@@ -111,17 +177,17 @@ private Q_SLOTS:
<< EmptyList(QByteArray)
<< EmptyList(QString)
<< List(QByteArray, "testSession")
- << msg
+ << Protocol::ChangeNotification(itemMsg)
<< false;
// Simulate adding a new resource
- msg = Protocol::ChangeNotification();
- msg.setType(Protocol::ChangeNotification::Collections);
- msg.setOperation(Protocol::ChangeNotification::Add);
- msg.addEntity(1, QStringLiteral("imap://user@some.domain/"));
- msg.setParentCollection(0);
- msg.setSessionId("akonadi_imap_resource_0");
- msg.setResource("akonadi_imap_resource_0");
+ Protocol::CollectionChangeNotification colMsg;
+ colMsg.setOperation(Protocol::CollectionChangeNotification::Add);
+ colMsg.setId(1);
+ colMsg.setRemoteId(QStringLiteral("imap://user@some.domain/"));
+ colMsg.setParentCollection(0);
+ colMsg.setSessionId("akonadi_imap_resource_0");
+ colMsg.setResource("akonadi_imap_resource_0");
QTest::newRow("new root collection in non-monitored resource")
<< false
<< List(Entity::Id, 0)
@@ -129,18 +195,17 @@ private Q_SLOTS:
<< List(QByteArray, "akonadi_search_resource")
<< List(QString, QStringLiteral("message/rfc822"))
<< EmptyList(QByteArray)
- << msg
+ << Protocol::ChangeNotification(colMsg)
<< true;
- msg = Protocol::ChangeNotification();
- msg.setType(Protocol::ChangeNotification::Items);
- msg.setOperation(Protocol::ChangeNotification::Move);
- msg.setResource("akonadi_resource_1");
- msg.setDestinationResource("akonadi_resource_2");
- msg.setParentCollection(1);
- msg.setParentDestCollection(2);
- msg.setSessionId("kmail");
- msg.addEntity(10, QStringLiteral("123"), QStringLiteral("1"), QStringLiteral("message/rfc822"));
+ itemMsg = Protocol::ItemChangeNotification();
+ itemMsg.setOperation(Protocol::ItemChangeNotification::Move);
+ itemMsg.setResource("akonadi_resource_1");
+ itemMsg.setDestinationResource("akonadi_resource_2");
+ itemMsg.setParentCollection(1);
+ itemMsg.setParentDestCollection(2);
+ itemMsg.setSessionId("kmail");
+ itemMsg.addItem(10, QStringLiteral("123"), QStringLiteral("1"), QStringLiteral("message/rfc822"));
QTest::newRow("inter-resource move, source source")
<< false
<< EmptyList(Entity::Id)
@@ -148,7 +213,7 @@ private Q_SLOTS:
<< List(QByteArray, "akonadi_resource_1")
<< List(QString, QStringLiteral("message/rfc822"))
<< List(QByteArray, "akonadi_resource_1")
- << msg
+ << Protocol::ChangeNotification(itemMsg)
<< true;
QTest::newRow("inter-resource move, destination source")
@@ -158,7 +223,7 @@ private Q_SLOTS:
<< List(QByteArray, "akonadi_resource_2")
<< List(QString, QStringLiteral("message/rfc822"))
<< List(QByteArray, "akonadi_resource_2")
- << msg
+ << Protocol::ChangeNotification(itemMsg)
<< true;
QTest::newRow("inter-resource move, uninterested party")
@@ -168,15 +233,33 @@ private Q_SLOTS:
<< EmptyList(QByteArray)
<< List(QString, QStringLiteral("inode/directory"))
<< EmptyList(QByteArray)
- << msg
+ << Protocol::ChangeNotification(itemMsg)
<< false;
- msg = Protocol::ChangeNotification();
- msg.setType(Protocol::ChangeNotification::Collections);
- msg.setOperation(Protocol::ChangeNotification::Add);
- msg.setSessionId("kmail");
- msg.setResource("akonadi_resource_1");
- msg.setParentCollection(1);
+ itemMsg = Protocol::ItemChangeNotification();
+ itemMsg.setOperation(Protocol::ItemChangeNotification::Move);
+ itemMsg.setResource("akonadi_resource_0");
+ itemMsg.setDestinationResource("akonadi_resource_0");
+ itemMsg.setParentCollection(1);
+ itemMsg.setParentDestCollection(2);
+ itemMsg.setSessionId("kmail");
+ itemMsg.addItem(10, QStringLiteral("123"), QStringLiteral("1"), QStringLiteral("message/rfc822"));
+ itemMsg.addItem(11, QStringLiteral("456"), QStringLiteral("1"), QStringLiteral("message/rfc822"));
+ QTest::newRow("intra-resource move, owning resource")
+ << false
+ << EmptyList(Entity::Id)
+ << EmptyList(Entity::Id)
+ << List(QByteArray, "akonadi_imap_resource_0")
+ << List(QString, QStringLiteral("message/rfc822"))
+ << List(QByteArray, "akonadi_imap_resource_0")
+ << Protocol::ChangeNotification(itemMsg)
+ << true;
+
+ colMsg = Protocol::CollectionChangeNotification();
+ colMsg.setOperation(Protocol::CollectionChangeNotification::Add);
+ colMsg.setSessionId("kmail");
+ colMsg.setResource("akonadi_resource_1");
+ colMsg.setParentCollection(1);
QTest::newRow("new subfolder")
<< false
<< List(Entity::Id, 0)
@@ -184,16 +267,15 @@ private Q_SLOTS:
<< EmptyList(QByteArray)
<< List(QString, QStringLiteral("message/rfc822"))
<< EmptyList(QByteArray)
- << msg
+ << Protocol::ChangeNotification(colMsg)
<< false;
- msg = Protocol::ChangeNotification();
- msg.setType(Protocol::ChangeNotification::Items);
- msg.setOperation(Protocol::ChangeNotification::Add);
- msg.setSessionId("randomSession");
- msg.setResource("randomResource");
- msg.setParentCollection(1);
- msg.addEntity(10, QString(), QString(), QStringLiteral("message/rfc822"));
+ itemMsg = Protocol::ItemChangeNotification();
+ itemMsg.setOperation(Protocol::ItemChangeNotification::Add);
+ itemMsg.setSessionId("randomSession");
+ itemMsg.setResource("randomResource");
+ itemMsg.setParentCollection(1);
+ itemMsg.addItem(10, QString(), QString(), QStringLiteral("message/rfc822"));
QTest::newRow("new mail for mailfilter or maildispatcher")
<< false
<< List(Entity::Id, 0)
@@ -201,59 +283,59 @@ private Q_SLOTS:
<< EmptyList(QByteArray)
<< List(QString, QStringLiteral("message/rfc822"))
<< EmptyList(QByteArray)
- << msg
+ << Protocol::ChangeNotification(itemMsg)
<< true;
- msg = Protocol::ChangeNotification();
- msg.setType( Protocol::ChangeNotification::Tags );
- msg.setOperation( Protocol::ChangeNotification::Remove );
- msg.setSessionId( "randomSession" );
- msg.setResource( "akonadi_random_resource_0" );
- msg.addEntity( 1, QStringLiteral("TAG") );
- QTest::newRow( "Tag removal - resource notification - matching resource source")
- << false
- << EmptyList( Entity::Id )
- << EmptyList( Entity::Id )
- << EmptyList( QByteArray )
- << EmptyList( QString )
- << List( QByteArray, "akonadi_random_resource_0" )
- << msg
- << true;
-
- QTest::newRow( "Tag removal - resource notification - wrong resource source" )
- << false
- << EmptyList( Entity::Id )
- << EmptyList( Entity::Id )
- << EmptyList( QByteArray )
- << EmptyList( QString )
- << List( QByteArray, "akonadi_another_resource_1" )
- << msg
- << false;
-
- msg = Protocol::ChangeNotification();
- msg.setType( Protocol::ChangeNotification::Tags );
- msg.setOperation( Protocol::ChangeNotification::Remove );
- msg.setSessionId( "randomSession" );
- msg.addEntity( 1, QStringLiteral("TAG") );
- QTest::newRow( "Tag removal - client notification - client source" )
- << false
- << EmptyList( Entity::Id )
- << EmptyList( Entity::Id )
- << EmptyList( QByteArray )
- << EmptyList( QString )
- << EmptyList( QByteArray )
- << msg
- << true;
-
- QTest::newRow( "Tag removal - client notification - resource source" )
- << false
- << EmptyList( Entity::Id )
- << EmptyList( Entity::Id )
- << EmptyList( QByteArray )
- << EmptyList( QString )
- << List( QByteArray, "akonadi_some_resource_0" )
- << msg
- << false;
+ Protocol::TagChangeNotification tagMsg;
+ tagMsg.setOperation(Protocol::TagChangeNotification::Remove);
+ tagMsg.setSessionId("randomSession");
+ tagMsg.setResource("akonadi_random_resource_0");
+ tagMsg.setId(1);
+ tagMsg.setRemoteId(QStringLiteral("TAG"));
+ QTest::newRow("Tag removal - resource notification - matching resource source")
+ << false
+ << EmptyList(Entity::Id)
+ << EmptyList(Entity::Id)
+ << EmptyList(QByteArray)
+ << EmptyList(QString)
+ << List(QByteArray, "akonadi_random_resource_0")
+ << Protocol::ChangeNotification(tagMsg)
+ << true;
+
+ QTest::newRow("Tag removal - resource notification - wrong resource source")
+ << false
+ << EmptyList(Entity::Id)
+ << EmptyList(Entity::Id)
+ << EmptyList(QByteArray)
+ << EmptyList(QString)
+ << List(QByteArray, "akonadi_another_resource_1")
+ << Protocol::ChangeNotification(tagMsg)
+ << false;
+
+ tagMsg = Protocol::TagChangeNotification();
+ tagMsg.setOperation(Protocol::TagChangeNotification::Remove);
+ tagMsg.setSessionId("randomSession");
+ tagMsg.setId(1);
+ tagMsg.setRemoteId(QStringLiteral("TAG"));
+ QTest::newRow("Tag removal - client notification - client source")
+ << false
+ << EmptyList(Entity::Id)
+ << EmptyList(Entity::Id)
+ << EmptyList(QByteArray)
+ << EmptyList(QString)
+ << EmptyList(QByteArray)
+ << Protocol::ChangeNotification(tagMsg)
+ << true;
+
+ QTest::newRow("Tag removal - client notification - resource source")
+ << false
+ << EmptyList(Entity::Id)
+ << EmptyList(Entity::Id)
+ << EmptyList(QByteArray)
+ << EmptyList(QString)
+ << List( QByteArray, "akonadi_some_resource_0" )
+ << Protocol::ChangeNotification(tagMsg)
+ << false;
}
void testSourceFilter()
@@ -267,37 +349,31 @@ private Q_SLOTS:
QFETCH(Protocol::ChangeNotification, notification);
QFETCH(bool, accepted);
- NotificationManager mgr;
- NotificationSource source(QStringLiteral("testSource"), QString(), &mgr);
- mgr.registerSource(&source);
+ TestableNotificationSubscriber subscriber;
- source.setAllMonitored(allMonitored);
+ subscriber.setAllMonitored(allMonitored);
Q_FOREACH (Entity::Id id, monitoredCollections) {
- source.setMonitoredCollection(id, true);
+ subscriber.setMonitoredCollection(id, true);
}
Q_FOREACH (Entity::Id id, monitoredItems) {
- source.setMonitoredItem(id, true);
+ subscriber.setMonitoredItem(id, true);
}
Q_FOREACH (const QByteArray &res, monitoredResources) {
- source.setMonitoredResource(res, true);
+ subscriber.setMonitoredResource(res, true);
}
Q_FOREACH (const QString &mimeType, monitoredMimeTypes) {
- source.setMonitoredMimeType(mimeType, true);
+ subscriber.setMonitoredMimeType(mimeType, true);
}
Q_FOREACH (const QByteArray &session, ignoredSessions) {
- source.setIgnoredSession(session, true);
+ subscriber.setIgnoredSession(session, true);
}
- QSignalSpy spy(&source, SIGNAL(notify(Akonadi::Protocol::Command)));
- Protocol::ChangeNotification::List list;
- list << notification;
- mgr.slotNotify(list);
- mgr.emitPendingNotifications();
+ subscriber.notify({ notification });
- QCOMPARE(spy.count(), accepted ? 1 : 0);
+ QTRY_COMPARE(subscriber.emittedNotifications.count(), accepted ? 1 : 0);
if (accepted) {
- Protocol::ChangeNotification ntf = spy.at(0).at(0).value<Protocol::Command>();
+ const Protocol::ChangeNotification ntf = subscriber.emittedNotifications.at(0);
QVERIFY(ntf.isValid());
}
}
diff --git a/autotests/server/relationhandlertest.cpp b/autotests/server/relationhandlertest.cpp
index 8ff7f07..bb764c3 100644
--- a/autotests/server/relationhandlertest.cpp
+++ b/autotests/server/relationhandlertest.cpp
@@ -87,15 +87,17 @@ public:
QScopedPointer<DbInitializer> initializer;
- Akonadi::Protocol::ChangeNotification relationNotification(const Akonadi::Protocol::ChangeNotification &notificationTemplate, const PimItem &item1, const PimItem &item2, const QByteArray &rid, const QByteArray &type = QByteArray("type"))
+ Protocol::RelationChangeNotification relationNotification(const Protocol::RelationChangeNotification &notificationTemplate,
+ const PimItem &item1,
+ const PimItem &item2,
+ const QString &rid,
+ const QString &type = QStringLiteral("type"))
{
- Akonadi::Protocol::ChangeNotification notification = notificationTemplate;
- QSet<QByteArray> itemParts;
- itemParts << "LEFT " + QByteArray::number(item1.id());
- itemParts << "RIGHT " + QByteArray::number(item2.id());
- itemParts << "TYPE " + type;
- itemParts << "RID " + rid;
- notification.setItemParts(itemParts);
+ Protocol::RelationChangeNotification notification = notificationTemplate;
+ notification.setLeftItem(item1.id());
+ notification.setRightItem(item2.id());
+ notification.setRemoteId(rid);
+ notification.setType(type);
return notification;
}
@@ -110,11 +112,10 @@ private Q_SLOTS:
QTest::addColumn<TestScenario::List >("scenarios");
QTest::addColumn<Relation::List>("expectedRelations");
- QTest::addColumn<Akonadi::Protocol::ChangeNotification::List>("expectedNotifications");
+ QTest::addColumn<Protocol::ChangeNotification::List>("expectedNotifications");
- Akonadi::Protocol::ChangeNotification notificationTemplate;
- notificationTemplate.setType(Protocol::ChangeNotification::Relations);
- notificationTemplate.setOperation(Protocol::ChangeNotification::Add);
+ Protocol::RelationChangeNotification notificationTemplate;
+ notificationTemplate.setOperation(Protocol::RelationChangeNotification::Add);
notificationTemplate.setSessionId(FakeAkonadiServer::instanceName().toLatin1());
{
@@ -130,17 +131,16 @@ private Q_SLOTS:
type.setName(QLatin1String("type"));
rel.setRelationType(type);
- Akonadi::Protocol::ChangeNotification itemNotification;
- itemNotification.setType(Protocol::ChangeNotification::Items);
- itemNotification.setOperation(Protocol::ChangeNotification::ModifyRelations);
+ Protocol::ItemChangeNotification itemNotification;
+ itemNotification.setOperation(Protocol::ItemChangeNotification::ModifyRelations);
itemNotification.setSessionId(FakeAkonadiServer::instanceName().toLatin1());
itemNotification.setResource("testresource");
itemNotification.setParentCollection(col1.id());
- itemNotification.addEntity(item1.id(), item1.remoteId(), QString(), item1.mimeType().name());
- itemNotification.addEntity(item2.id(), item2.remoteId(), QString(), item2.mimeType().name());
- itemNotification.setAddedFlags(QSet<QByteArray>() << "RELATION type " + QByteArray::number(item1.id()) + " " + QByteArray::number(item2.id()));
+ itemNotification.addItem(item1.id(), item1.remoteId(), QString(), item1.mimeType().name());
+ itemNotification.addItem(item2.id(), item2.remoteId(), QString(), item2.mimeType().name());
+ itemNotification.setAddedRelations({ Protocol::ItemChangeNotification::Relation(item1.id(), item2.id(), QStringLiteral("type")) });
- Akonadi::Protocol::ChangeNotification notification = relationNotification(notificationTemplate, item1, item2, rel.remoteId().toLatin1());
+ Protocol::ChangeNotification notification = relationNotification(notificationTemplate, item1, item2, rel.remoteId());
QTest::newRow("uid create relation") << scenarios << (Relation::List() << rel) << (Protocol::ChangeNotification::List() << notification << itemNotification);
}
@@ -197,31 +197,29 @@ private Q_SLOTS:
rel2.setRelationType(RelationType::retrieveByName(QLatin1String("type2")));
QVERIFY(rel2.insert());
- Akonadi::Protocol::ChangeNotification notificationTemplate;
- notificationTemplate.setType(Protocol::ChangeNotification::Relations);
- notificationTemplate.setOperation(Protocol::ChangeNotification::Remove);
+ Protocol::RelationChangeNotification notificationTemplate;
+ notificationTemplate.setOperation(Protocol::RelationChangeNotification::Remove);
notificationTemplate.setSessionId(FakeAkonadiServer::instanceName().toLatin1());
QTest::addColumn<TestScenario::List>("scenarios");
QTest::addColumn<Relation::List>("expectedRelations");
- QTest::addColumn<Akonadi::Protocol::ChangeNotification::List>("expectedNotifications");
+ QTest::addColumn<Protocol::ChangeNotification::List>("expectedNotifications");
{
TestScenario::List scenarios;
scenarios << FakeAkonadiServer::loginScenario()
<< TestScenario::create(5, TestScenario::ClientCmd, Protocol::RemoveRelationsCommand(item1.id(), item2.id(), "type"))
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::RemoveRelationsResponse());
- Akonadi::Protocol::ChangeNotification itemNotification;
- itemNotification.setType(Protocol::ChangeNotification::Items);
- itemNotification.setOperation(Protocol::ChangeNotification::ModifyRelations);
+ Protocol::ItemChangeNotification itemNotification;
+ itemNotification.setOperation(Protocol::ItemChangeNotification::ModifyRelations);
itemNotification.setSessionId(FakeAkonadiServer::instanceName().toLatin1());
itemNotification.setResource("testresource");
itemNotification.setParentCollection(col1.id());
- itemNotification.addEntity(item1.id(), item1.remoteId(), QString(), item1.mimeType().name());
- itemNotification.addEntity(item2.id(), item2.remoteId(), QString(), item2.mimeType().name());
- itemNotification.setRemovedFlags(QSet<QByteArray>() << "RELATION type " + QByteArray::number(item1.id()) + " " + QByteArray::number(item2.id()));
+ itemNotification.addItem(item1.id(), item1.remoteId(), QString(), item1.mimeType().name());
+ itemNotification.addItem(item2.id(), item2.remoteId(), QString(), item2.mimeType().name());
+ itemNotification.setRemovedRelations({ Protocol::ItemChangeNotification::Relation(item1.id(), item2.id(), QStringLiteral("type")) });
- Akonadi::Protocol::ChangeNotification notification = relationNotification(notificationTemplate, item1, item2, rel.remoteId().toLatin1());
+ Protocol::ChangeNotification notification = relationNotification(notificationTemplate, item1, item2, rel.remoteId());
QTest::newRow("uid remove relation") << scenarios << (Relation::List() << rel2) << (Protocol::ChangeNotification::List() << notification << itemNotification);
}
@@ -232,17 +230,16 @@ private Q_SLOTS:
<< TestScenario::create(5, TestScenario::ClientCmd, Protocol::RemoveRelationsCommand(item1.id(), item2.id()))
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::RemoveRelationsResponse());
- Akonadi::Protocol::ChangeNotification itemNotification;
- itemNotification.setType(Protocol::ChangeNotification::Items);
- itemNotification.setOperation(Protocol::ChangeNotification::ModifyRelations);
+ Protocol::ItemChangeNotification itemNotification;
+ itemNotification.setOperation(Protocol::ItemChangeNotification::ModifyRelations);
itemNotification.setSessionId(FakeAkonadiServer::instanceName().toLatin1());
itemNotification.setResource("testresource");
itemNotification.setParentCollection(col1.id());
- itemNotification.addEntity(item1.id(), item1.remoteId(), QString(), item1.mimeType().name());
- itemNotification.addEntity(item2.id(), item2.remoteId(), QString(), item2.mimeType().name());
- itemNotification.setRemovedFlags(QSet<QByteArray>() << "RELATION type2 " + QByteArray::number(item1.id()) + " " + QByteArray::number(item2.id()));
+ itemNotification.addItem(item1.id(), item1.remoteId(), QString(), item1.mimeType().name());
+ itemNotification.addItem(item2.id(), item2.remoteId(), QString(), item2.mimeType().name());
+ itemNotification.setRemovedRelations({ Protocol::ItemChangeNotification::Relation(item1.id(), item2.id(), QStringLiteral("type2")) });
- Akonadi::Protocol::ChangeNotification notification = relationNotification(notificationTemplate, item1, item2, rel.remoteId().toLatin1(), "type2");
+ Protocol::ChangeNotification notification = relationNotification(notificationTemplate, item1, item2, rel.remoteId(), QStringLiteral("type2"));
QTest::newRow("uid remove relation without type") << scenarios << Relation::List() << (Protocol::ChangeNotification::List() << notification << itemNotification);
}
diff --git a/autotests/server/taghandlertest.cpp b/autotests/server/taghandlertest.cpp
index 884bceb..66041e6 100644
--- a/autotests/server/taghandlertest.cpp
+++ b/autotests/server/taghandlertest.cpp
@@ -142,11 +142,10 @@ private Q_SLOTS:
attribute.setType("TAG");
attribute.setValue("(\\\"tag2\\\" \\\"\\\" \\\"\\\" \\\"\\\" \\\"0\\\" () () \\\"-1\\\")");
- Akonadi::Protocol::ChangeNotification notification;
- notification.setType(Protocol::ChangeNotification::Tags);
- notification.setOperation(Protocol::ChangeNotification::Add);
+ Akonadi::Protocol::TagChangeNotification notification;
+ notification.setOperation(Protocol::TagChangeNotification::Add);
notification.setSessionId(FakeAkonadiServer::instanceName().toLatin1());
- notification.addEntity(1);
+ notification.setId(1);
QTest::newRow("uid create relation") << scenarios
<< QVector<TagTagAttributeListPair>{ { tag, { attribute } } }
@@ -182,11 +181,10 @@ private Q_SLOTS:
attribute.setType("TAG");
attribute.setValue("(\\\"tag3\\\" \\\"\\\" \\\"\\\" \\\"\\\" \\\"0\\\" () () \\\"-1\\\")");
- Akonadi::Protocol::ChangeNotification notification;
- notification.setType(Protocol::ChangeNotification::Tags);
- notification.setOperation(Protocol::ChangeNotification::Add);
+ Akonadi::Protocol::TagChangeNotification notification;
+ notification.setOperation(Protocol::TagChangeNotification::Add);
notification.setSessionId(FakeAkonadiServer::instanceName().toLatin1());
- notification.addEntity(2);
+ notification.setId(2);
QTest::newRow("create child tag") << scenarios
<< QVector<TagTagAttributeListPair>{ { tag, { attribute } } }
@@ -209,9 +207,7 @@ private Q_SLOTS:
QCOMPARE(receivedNotifications.size(), expectedNotifications.count());
for (int i = 0; i < expectedNotifications.size(); i++) {
QCOMPARE(receivedNotifications.at(i), expectedNotifications.at(i));
- Q_FOREACH (qint64 id, receivedNotifications.at(i).entities().keys()) {
- ids << id;
- }
+ ids << Protocol::TagChangeNotification(receivedNotifications.at(i)).id();
}
SelectQueryBuilder<Tag> qb;
@@ -279,11 +275,10 @@ private Q_SLOTS:
<< TestScenario::create(5, TestScenario::ServerCmd, createResponse(tag, QByteArray(), cmd.attributes()))
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyTagResponse());
- Akonadi::Protocol::ChangeNotification notification;
- notification.setType(Protocol::ChangeNotification::Tags);
- notification.setOperation(Protocol::ChangeNotification::Modify);
+ Akonadi::Protocol::TagChangeNotification notification;
+ notification.setOperation(Protocol::TagChangeNotification::Modify);
notification.setSessionId(FakeAkonadiServer::instanceName().toLatin1());
- notification.addEntity(tag.id());
+ notification.setId(tag.id());
QTest::newRow("uid store name") << scenarios << (Tag::List() << tag) << (Protocol::ChangeNotification::List() << notification);
}
@@ -347,20 +342,18 @@ private Q_SLOTS:
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::DeleteTagResponse())
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyTagResponse());
- Akonadi::Protocol::ChangeNotification itemUntaggedNtf;
- itemUntaggedNtf.setType(Protocol::ChangeNotification::Items);
- itemUntaggedNtf.setOperation(Protocol::ChangeNotification::ModifyTags);
+ Akonadi::Protocol::ItemChangeNotification itemUntaggedNtf;
+ itemUntaggedNtf.setOperation(Protocol::ItemChangeNotification::ModifyTags);
itemUntaggedNtf.setSessionId(FakeAkonadiServer::instanceName().toLatin1());
- itemUntaggedNtf.addEntity(pimItem.id(), pimItem.remoteId(), QString(), pimItem.mimeType().name());
+ itemUntaggedNtf.addItem(pimItem.id(), pimItem.remoteId(), QString(), pimItem.mimeType().name());
itemUntaggedNtf.setResource(res2.name().toLatin1());
itemUntaggedNtf.setParentCollection(col.id());
itemUntaggedNtf.setRemovedTags(QSet<qint64>() << tag.id());
- Akonadi::Protocol::ChangeNotification tagRemoveNtf;
- tagRemoveNtf.setType(Protocol::ChangeNotification::Tags);
- tagRemoveNtf.setOperation(Protocol::ChangeNotification::Remove);
+ Akonadi::Protocol::TagChangeNotification tagRemoveNtf;
+ tagRemoveNtf.setOperation(Protocol::TagChangeNotification::Remove);
tagRemoveNtf.setSessionId(FakeAkonadiServer::instanceName().toLatin1());
- tagRemoveNtf.addEntity(tag.id());
+ tagRemoveNtf.setId(tag.id());
QTest::newRow("uid store unset last rid") << scenarios << Tag::List() << (Protocol::ChangeNotification::List() << itemUntaggedNtf << tagRemoveNtf);
}
@@ -425,21 +418,22 @@ private Q_SLOTS:
<< TestScenario::create(5, TestScenario::ClientCmd, Protocol::DeleteTagCommand(tag.id()))
<< TestScenario::create(5, TestScenario::ServerCmd, Protocol::DeleteTagResponse());
- Akonadi::Protocol::ChangeNotification ntf;
- ntf.setType(Protocol::ChangeNotification::Tags);
- ntf.setOperation(Protocol::ChangeNotification::Remove);
+ Akonadi::Protocol::TagChangeNotification ntf;
+ ntf.setOperation(Protocol::TagChangeNotification::Remove);
ntf.setSessionId(FakeAkonadiServer::instanceName().toLatin1());
- Akonadi::Protocol::ChangeNotification res1Ntf = ntf;
- res1Ntf.addEntity(tag.id(), rel1.remoteId());
+ Akonadi::Protocol::TagChangeNotification res1Ntf = ntf;
+ res1Ntf.setId(tag.id());
+ res1Ntf.setRemoteId(rel1.remoteId());
res1Ntf.setResource(res1.name().toLatin1());
- Akonadi::Protocol::ChangeNotification res2Ntf = ntf;
- res2Ntf.addEntity(tag.id(), rel2.remoteId());
+ Akonadi::Protocol::TagChangeNotification res2Ntf = ntf;
+ res2Ntf.setId(tag.id());
+ res2Ntf.setRemoteId(rel2.remoteId());
res2Ntf.setResource(res2.name().toLatin1());
- Akonadi::Protocol::ChangeNotification clientNtf = ntf;
- clientNtf.addEntity(tag.id());
+ Akonadi::Protocol::TagChangeNotification clientNtf = ntf;
+ clientNtf.setId(tag.id());
QTest::newRow("uid remove") << scenarios << Tag::List() << (Protocol::ChangeNotification::List() << res1Ntf << res2Ntf << clientNtf);
}
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 2e3496b..2dca11d 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -11,7 +11,7 @@ set(akonadicore_base_SRCS
changenotificationdependenciesfactory.cpp
changerecorder.cpp
changerecorder_p.cpp
- connectionthread.cpp
+ connection.cpp
collection.cpp
collectioncolorattribute.cpp
collectionfetchscope.cpp
@@ -46,7 +46,6 @@ set(akonadicore_base_SRCS
monitor.cpp
monitor_p.cpp
newmailnotifierattribute.cpp
- notificationbus_p.cpp
notificationsource_p.cpp
partfetcher.cpp
pastehelper.cpp
@@ -59,8 +58,10 @@ set(akonadicore_base_SRCS
searchquery.cpp
servermanager.cpp
session.cpp
+ sessionthread.cpp
specialcollectionattribute.cpp
specialcollections.cpp
+ subscriptionmonitor.cpp
tag.cpp
tagattribute.cpp
tagfetchscope.cpp
@@ -114,6 +115,7 @@ ecm_generate_headers(AkonadiCore_base_HEADERS
SpecialCollections
SpecialCollectionAttribute
Supertrait
+ SubscriptionMonitor
Tag
TagAttribute
TagFetchScope
diff --git a/src/core/changenotificationdependenciesfactory.cpp b/src/core/changenotificationdependenciesfactory.cpp
index d9baf40..5f039fe 100644
--- a/src/core/changenotificationdependenciesfactory.cpp
+++ b/src/core/changenotificationdependenciesfactory.cpp
@@ -18,70 +18,25 @@
*/
#include "changenotificationdependenciesfactory_p.h"
-#include "KDBusConnectionPool"
-#include "notificationsource_p.h"
-#include "notificationbus_p.h"
-#include "notificationsourceinterface.h"
-#include "notificationmanagerinterface.h"
+#include "sessionthread_p.h"
+#include "connection_p.h"
#include "changemediator_p.h"
#include "servermanager.h"
#include "akonadicore_debug.h"
+#include "session_p.h"
#include <KRandom>
#include <qdbusextratypes.h>
using namespace Akonadi;
-NotificationSource *ChangeNotificationDependenciesFactory::createNotificationSource(QObject *parent)
+Connection *ChangeNotificationDependenciesFactory::createNotificationConnection(Session *session)
{
if (!Akonadi::ServerManager::self()->isRunning()) {
return 0;
}
- org::freedesktop::Akonadi::NotificationManager *manager =
- new org::freedesktop::Akonadi::NotificationManager(
- ServerManager::serviceName(Akonadi::ServerManager::Server),
- QStringLiteral("/notifications"),
- KDBusConnectionPool::threadConnection());
-
- if (!manager) {
- // :TODO: error handling
- return 0;
- }
-
- const QString name =
- QStringLiteral("%1_%2_%3").arg(
- QCoreApplication::applicationName(),
- QString::number(QCoreApplication::applicationPid()),
- KRandom::randomString(6));
- QDBusObjectPath p = manager->subscribe(name, false);
- const bool validError = manager->lastError().isValid();
- if (validError) {
- qCWarning(AKONADICORE_LOG) << manager->lastError().name() << manager->lastError().message();
- // :TODO: What to do?
- delete manager;
- return 0;
- }
- delete manager;
- org::freedesktop::Akonadi::NotificationSource *notificationSource =
- new org::freedesktop::Akonadi::NotificationSource(
- ServerManager::serviceName(Akonadi::ServerManager::Server),
- p.path(),
- KDBusConnectionPool::threadConnection(), parent);
-
- if (!notificationSource) {
- // :TODO: error handling
- return 0;
- }
- return new NotificationSource(notificationSource);
-}
-
-QObject *ChangeNotificationDependenciesFactory::createNotificationBus(QObject *parent, NotificationSource *source)
-{
- NotificationBusPrivate *priv = new NotificationBusPrivate;
- Session *session = new Session(priv, source->identifier().toLatin1(), parent);
- priv->setParent(session);
- return priv;
+ return session->d->sessionThread()->createConnection(Connection::NotificationConnection, session->sessionId());
}
QObject *ChangeNotificationDependenciesFactory::createChangeMediator(QObject *parent)
diff --git a/src/core/changenotificationdependenciesfactory_p.h b/src/core/changenotificationdependenciesfactory_p.h
index 414984f..141a679 100644
--- a/src/core/changenotificationdependenciesfactory_p.h
+++ b/src/core/changenotificationdependenciesfactory_p.h
@@ -26,7 +26,7 @@
namespace Akonadi
{
-class NotificationSource;
+class Connection;
/**
* This class exists so that we can create a fake notification source in
@@ -38,8 +38,7 @@ public:
virtual ~ChangeNotificationDependenciesFactory()
{
}
- virtual NotificationSource *createNotificationSource(QObject *parent);
- virtual QObject *createNotificationBus(QObject *parent, NotificationSource *source);
+ virtual Connection *createNotificationConnection(Session *parent);
virtual QObject *createChangeMediator(QObject *parent);
virtual Akonadi::CollectionCache *createCollectionCache(int maxCapacity, Session *session);
diff --git a/src/core/changerecorder_p.cpp b/src/core/changerecorder_p.cpp
index e3ce060..68cb16e 100644
--- a/src/core/changerecorder_p.cpp
+++ b/src/core/changerecorder_p.cpp
@@ -87,23 +87,22 @@ void ChangeRecorderPrivate::loadNotifications()
for (int i = 0; i < size; ++i) {
settings->setArrayIndex(i);
Protocol::ChangeNotification msg;
- msg.setSessionId(settings->value(QStringLiteral("sessionId")).toByteArray());
- msg.setType((Protocol::ChangeNotification::Type)settings->value(QStringLiteral("type")).toInt());
- msg.setOperation((Protocol::ChangeNotification::Operation)settings->value(QStringLiteral("op")).toInt());
- msg.addEntity(settings->value(QStringLiteral("uid")).toLongLong(),
- settings->value(QStringLiteral("rid")).toString(),
- QString(),
- settings->value(QStringLiteral("mimeType")).toString());
- msg.setResource(settings->value(QStringLiteral("resource")).toByteArray());
- msg.setParentCollection(settings->value(QStringLiteral("parentCol")).toLongLong());
- msg.setParentDestCollection(settings->value(QStringLiteral("parentDestCol")).toLongLong());
- list = settings->value(QStringLiteral("itemParts")).toStringList();
- QSet<QByteArray> itemParts;
- Q_FOREACH (const QString &entry, list) {
- itemParts.insert(entry.toLatin1());
+
+ switch (static_cast<LegacyType>(settings->value(QStringLiteral("type")).toInt())) {
+ case Item:
+ msg = loadItemNotification(settings);
+ break;
+ case Collection:
+ msg = loadCollectionNotification(settings);
+ case Tag:
+ case Relation:
+ case InvalidType:
+ qWarning() << "Unexpected notification type in legacy store";
+ continue;
+ }
+ if (msg.isValid()) {
+ pendingNotifications << msg;
}
- msg.setItemParts(itemParts);
- pendingNotifications << msg;
}
settings->endArray();
@@ -128,7 +127,7 @@ void ChangeRecorderPrivate::loadNotifications()
notificationsLoaded();
}
-static const quint64 s_currentVersion = Q_UINT64_C(0x000400000000);
+static const quint64 s_currentVersion = Q_UINT64_C(0x000500000000);
static const quint64 s_versionMask = Q_UINT64_C(0xFFFF00000000);
static const quint64 s_sizeMask = Q_UINT64_C(0x0000FFFFFFFF);
@@ -137,12 +136,8 @@ QQueue<Protocol::ChangeNotification> ChangeRecorderPrivate::loadFrom(QIODevice *
QDataStream stream(device);
stream.setVersion(QDataStream::Qt_4_6);
- QByteArray sessionId, resource, destinationResource;
- int type, operation, entityCnt;
- quint64 uid, parentCollection, parentDestCollection;
- QString remoteId, mimeType, remoteRevision;
- QSet<QByteArray> itemParts, addedFlags, removedFlags;
- QSet<qint64> addedTags, removedTags;
+ QByteArray sessionId;
+ int type;
QQueue<Protocol::ChangeNotification> list;
@@ -163,76 +158,33 @@ QQueue<Protocol::ChangeNotification> ChangeRecorderPrivate::loadFrom(QIODevice *
for (quint64 i = 0; i < size && !stream.atEnd(); ++i) {
Protocol::ChangeNotification msg;
+ stream >> sessionId;
+ stream >> type;
- if (version == 1) {
- stream >> sessionId;
- stream >> type;
- stream >> operation;
- stream >> uid;
- stream >> remoteId;
- stream >> resource;
- stream >> parentCollection;
- stream >> parentDestCollection;
- stream >> mimeType;
- stream >> itemParts;
-
- if (i < startOffset) {
- continue;
- }
-
- msg.setSessionId(sessionId);
- msg.setType(static_cast<Protocol::ChangeNotification::Type>(type));
- msg.setOperation(static_cast<Protocol::ChangeNotification::Operation>(operation));
- msg.addEntity(uid, remoteId, QString(), mimeType);
- msg.setResource(resource);
- msg.setParentCollection(parentCollection);
- msg.setParentDestCollection(parentDestCollection);
- msg.setItemParts(itemParts);
-
- } else if (version >= 2) {
-
- Protocol::ChangeNotification msg;
-
- stream >> sessionId;
- stream >> type;
- stream >> operation;
- stream >> entityCnt;
- for (int j = 0; j < entityCnt; ++j) {
- stream >> uid;
- stream >> remoteId;
- stream >> remoteRevision;
- stream >> mimeType;
- msg.addEntity(uid, remoteId, remoteRevision, mimeType);
- }
- stream >> resource;
- stream >> destinationResource;
- stream >> parentCollection;
- stream >> parentDestCollection;
- stream >> itemParts;
- stream >> addedFlags;
- stream >> removedFlags;
- if (version >= 3) {
- stream >> addedTags;
- stream >> removedTags;
- }
+ switch (static_cast<LegacyType>(type)) {
+ case Item:
+ msg = loadItemNotification(stream, version);
+ break;
+ case Collection:
+ msg = loadCollectionNotification(stream, version);
+ break;
+ case Tag:
+ msg = loadTagNotification(stream, version);
+ break;
+ case Relation:
+ msg = loadRelationNotification(stream, version);
+ break;
+ default:
+ qWarning() << "Unknown notification type";
+ break;
+ }
- if (i < startOffset) {
- continue;
- }
+ if (i < startOffset) {
+ continue;
+ }
+ if (msg.isValid()) {
msg.setSessionId(sessionId);
- msg.setType(static_cast<Protocol::ChangeNotification::Type>(type));
- msg.setOperation(static_cast<Protocol::ChangeNotification::Operation>(operation));
- msg.setResource(resource);
- msg.setDestinationResource(destinationResource);
- msg.setParentCollection(parentCollection);
- msg.setParentDestCollection(parentDestCollection);
- msg.setItemParts(itemParts);
- msg.setAddedFlags(addedFlags);
- msg.setRemovedFlags(removedFlags);
- msg.setAddedTags(addedTags);
- msg.setRemovedTags(removedTags);
-
list << msg;
}
}
@@ -240,24 +192,6 @@ QQueue<Protocol::ChangeNotification> ChangeRecorderPrivate::loadFrom(QIODevice *
return list;
}
-static QString join(const QSet<QByteArray> &set)
-{
- QString string;
- Q_FOREACH (const QByteArray &b, set) {
- string += QString::fromLatin1(b) + QLatin1String(", ");
- }
- return string;
-}
-
-static QString join(const QList<qint64> &set)
-{
- QString string;
- Q_FOREACH (qint64 b, set) {
- string += QString::number(b) + QLatin1String(", ");
- }
- return string;
-}
-
QString ChangeRecorderPrivate::dumpNotificationListToString() const
{
if (!settings) {
@@ -274,78 +208,7 @@ QString ChangeRecorderPrivate::dumpNotificationListToString() const
bool dummy;
const QQueue<Protocol::ChangeNotification> notifications = loadFrom(&file, dummy);
Q_FOREACH (const Protocol::ChangeNotification &n, notifications) {
- QString typeString;
- switch (n.type()) {
- case Protocol::ChangeNotification::Collections:
- typeString = QStringLiteral("Collections");
- break;
- case Protocol::ChangeNotification::Items:
- typeString = QStringLiteral("Items");
- break;
- case Protocol::ChangeNotification::Tags:
- typeString = QStringLiteral("Tags");
- break;
- default:
- typeString = QStringLiteral("InvalidType");
- break;
- };
-
- QString operationString;
- switch (n.operation()) {
- case Protocol::ChangeNotification::Add:
- operationString = QStringLiteral("Add");
- break;
- case Protocol::ChangeNotification::Modify:
- operationString = QStringLiteral("Modify");
- break;
- case Protocol::ChangeNotification::ModifyFlags:
- operationString = QStringLiteral("ModifyFlags");
- break;
- case Protocol::ChangeNotification::ModifyTags:
- operationString = QStringLiteral("ModifyTags");
- break;
- case Protocol::ChangeNotification::Move:
- operationString = QStringLiteral("Move");
- break;
- case Protocol::ChangeNotification::Remove:
- operationString = QStringLiteral("Remove");
- break;
- case Protocol::ChangeNotification::Link:
- operationString = QStringLiteral("Link");
- break;
- case Protocol::ChangeNotification::Unlink:
- operationString = QStringLiteral("Unlink");
- break;
- case Protocol::ChangeNotification::Subscribe:
- operationString = QStringLiteral("Subscribe");
- break;
- case Protocol::ChangeNotification::Unsubscribe:
- operationString = QStringLiteral("Unsubscribe");
- break;
- default:
- operationString = QStringLiteral("InvalidOp");
- break;
- };
-
- const QString entities = join(n.entities().keys());
- const QString addedTags = join(n.addedTags().toList());
- const QString removedTags = join(n.removedTags().toList());
-
- const QString entry = QStringLiteral("session=%1 type=%2 operation=%3 items=%4 resource=%5 destResource=%6 parentCollection=%7 parentDestCollection=%8 itemParts=%9 addedFlags=%10 removedFlags=%11 addedTags=%12 removedTags=%13")
- .arg(QString::fromLatin1(n.sessionId()))
- .arg(typeString)
- .arg(operationString)
- .arg(entities)
- .arg(QString::fromLatin1(n.resource()))
- .arg(QString::fromLatin1(n.destinationResource()))
- .arg(n.parentCollection())
- .arg(n.parentDestCollection())
- .arg(join(n.itemParts()))
- .arg(join(n.addedFlags()))
- .arg(join(n.removedFlags()))
- .arg(addedTags)
- .arg(removedTags);
- result += entry + QLatin1Char('\n');
+ result += n.debugString() + QLatin1Char('\n');
}
return result;
}
@@ -356,24 +219,24 @@ void ChangeRecorderPrivate::addToStream(QDataStream &stream, const Protocol::Cha
// serialization format could change at any point
stream << msg.sessionId();
- stream << int(msg.type());
- stream << int(msg.operation());
- stream << msg.entities().count();
- Q_FOREACH (const Protocol::ChangeNotification::Entity &entity, msg.entities()) {
- stream << quint64(entity.id);
- stream << entity.remoteId;
- stream << entity.remoteRevision;
- stream << entity.mimeType;
+ stream << int(mapToLegacyType(msg.type()));
+ switch (msg.type()) {
+ case Protocol::Command::ItemChangeNotification:
+ saveItemNotification(stream, static_cast<const Protocol::ItemChangeNotification&>(msg));
+ break;
+ case Protocol::Command::CollectionChangeNotification:
+ saveCollectionNotification(stream, static_cast<const Protocol::CollectionChangeNotification&>(msg));
+ break;
+ case Protocol::Command::TagChangeNotification:
+ saveTagNotification(stream, static_cast<const Protocol::TagChangeNotification&>(msg));
+ break;
+ case Protocol::Command::RelationChangeNotification:
+ saveRelationNotification(stream, static_cast<const Protocol::RelationChangeNotification&>(msg));
+ break;
+ default:
+ qWarning() << "Unexpected type?";
+ return;
}
- stream << msg.resource();
- stream << msg.destinationResource();
- stream << quint64(msg.parentCollection());
- stream << quint64(msg.parentDestCollection());
- stream << msg.itemParts();
- stream << msg.addedFlags();
- stream << msg.removedFlags();
- stream << msg.addedTags();
- stream << msg.removedTags();
}
void ChangeRecorderPrivate::writeStartOffset()
@@ -505,3 +368,491 @@ bool ChangeRecorderPrivate::emitNotification(const Protocol::ChangeNotification
return someoneWasListening;
}
+
+Protocol::ChangeNotification ChangeRecorderPrivate::loadItemNotification(QSettings* settings) const
+{
+ Protocol::ItemChangeNotification msg;
+ msg.setSessionId(settings->value(QStringLiteral("sessionId")).toByteArray());
+ msg.setOperation(mapItemOperation(static_cast<LegacyOp>(settings->value(QStringLiteral("op")).toInt())));
+ msg.addItem(settings->value(QStringLiteral("uid")).toLongLong(),
+ settings->value(QStringLiteral("rid")).toString(),
+ QString(),
+ settings->value(QStringLiteral("mimeType")).toString());
+ msg.setResource(settings->value(QStringLiteral("resource")).toByteArray());
+ msg.setParentCollection(settings->value(QStringLiteral("parentCol")).toLongLong());
+ msg.setParentDestCollection(settings->value(QStringLiteral("parentDestCol")).toLongLong());
+ const QStringList list = settings->value(QStringLiteral("itemParts")).toStringList();
+ QSet<QByteArray> itemParts;
+ Q_FOREACH (const QString &entry, list) {
+ itemParts.insert(entry.toLatin1());
+ }
+ msg.setItemParts(itemParts);
+ return msg;
+}
+
+
+Protocol::ChangeNotification ChangeRecorderPrivate::loadCollectionNotification(QSettings* settings) const
+{
+ Protocol::CollectionChangeNotification msg;
+ msg.setSessionId(settings->value(QStringLiteral("sessionId")).toByteArray());
+ msg.setOperation(mapCollectionOperation(static_cast<LegacyOp>(settings->value(QStringLiteral("op")).toInt())));
+ msg.setId(settings->value(QStringLiteral("uid")).toLongLong());
+ msg.setRemoteId(settings->value(QStringLiteral("rid")).toString());
+ msg.setResource(settings->value(QStringLiteral("resource")).toByteArray());
+ msg.setParentCollection(settings->value(QStringLiteral("parentCol")).toLongLong());
+ msg.setParentDestCollection(settings->value(QStringLiteral("parentDestCol")).toLongLong());
+ const QStringList list = settings->value(QStringLiteral("itemParts")).toStringList();
+ QSet<QByteArray> changedParts;
+ Q_FOREACH (const QString &entry, list) {
+ changedParts.insert(entry.toLatin1());
+ }
+ msg.setChangedParts(changedParts);
+ return msg;
+}
+
+QSet<Protocol::ItemChangeNotification::Relation> ChangeRecorderPrivate::extractRelations(QSet<QByteArray> &flags) const
+{
+ QSet<Protocol::ItemChangeNotification::Relation> relations;
+ auto iter = flags.begin();
+ while (iter != flags.end()) {
+ if (iter->startsWith("RELATION")) {
+ const QByteArrayList parts = iter->split(' ');
+ Q_ASSERT(parts.size() == 4);
+ Protocol::ItemChangeNotification::Relation relation;
+ relation.type = QString::fromLatin1(parts[1]);
+ relation.leftId = parts[2].toLongLong();
+ relation.rightId = parts[3].toLongLong();
+ relations.insert(relation);
+ iter = flags.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+
+ return relations;
+}
+
+
+Protocol::ChangeNotification ChangeRecorderPrivate::loadItemNotification(QDataStream &stream, quint64 version) const
+{
+ QByteArray resource, destinationResource;
+ int operation, entityCnt;
+ quint64 uid, parentCollection, parentDestCollection;
+ QString remoteId, mimeType, remoteRevision;
+ QSet<QByteArray> itemParts, addedFlags, removedFlags;
+ QSet<qint64> addedTags, removedTags;
+
+ Protocol::ItemChangeNotification msg;
+
+ if (version == 1) {
+ stream >> operation;
+ stream >> uid;
+ stream >> remoteId;
+ stream >> resource;
+ stream >> parentCollection;
+ stream >> parentDestCollection;
+ stream >> mimeType;
+ stream >> itemParts;
+
+ msg.addItem(uid, remoteId, QString(), mimeType);
+ } else if (version >= 2) {
+ stream >> operation;
+ stream >> entityCnt;
+ for (int j = 0; j < entityCnt; ++j) {
+ stream >> uid;
+ stream >> remoteId;
+ stream >> remoteRevision;
+ stream >> mimeType;
+ msg.addItem(uid, remoteId, remoteRevision, mimeType);
+ }
+ stream >> resource;
+ stream >> destinationResource;
+ stream >> parentCollection;
+ stream >> parentDestCollection;
+ stream >> itemParts;
+ stream >> addedFlags;
+ stream >> removedFlags;
+ if (version >= 3) {
+ stream >> addedTags;
+ stream >> removedTags;
+ }
+ }
+ if (version >= 5) {
+ msg.setOperation(static_cast<Protocol::ItemChangeNotification::Operation>(operation));
+ } else {
+ msg.setOperation(mapItemOperation(static_cast<LegacyOp>(operation)));
+ }
+ msg.setResource(resource);
+ msg.setDestinationResource(destinationResource);
+ msg.setParentCollection(parentCollection);
+ msg.setParentDestCollection(parentDestCollection);
+ msg.setItemParts(itemParts);
+ msg.setAddedRelations(extractRelations(addedFlags));
+ msg.setAddedFlags(addedFlags);
+ msg.setRemovedRelations(extractRelations(removedFlags));
+ msg.setRemovedFlags(removedFlags);
+ msg.setAddedTags(addedTags);
+ msg.setRemovedTags(removedTags);
+ return msg;
+}
+
+QSet<QByteArray> ChangeRecorderPrivate::encodeRelations(const QSet<Protocol::ItemChangeNotification::Relation> &relations) const
+{
+ QSet<QByteArray> rv;
+ Q_FOREACH (const auto &rel, relations) {
+ rv.insert("RELATION " + rel.type.toLatin1() + " " + QByteArray::number(rel.leftId) + " " + QByteArray::number(rel.rightId));
+ }
+ return rv;
+}
+
+void ChangeRecorderPrivate::saveItemNotification(QDataStream &stream, const Protocol::ItemChangeNotification &msg)
+{
+ stream << int(msg.operation());
+ const auto items = msg.items();
+ stream << items.count();
+ Q_FOREACH (const Protocol::ItemChangeNotification::Item &item, items) {
+ stream << quint64(item.id);
+ stream << item.remoteId;
+ stream << item.remoteRevision;
+ stream << item.mimeType;
+ }
+ stream << msg.resource();
+ stream << msg.destinationResource();
+ stream << quint64(msg.parentCollection());
+ stream << quint64(msg.parentDestCollection());
+ stream << msg.itemParts();
+ stream << msg.addedFlags() + encodeRelations(msg.addedRelations());
+ stream << msg.removedFlags() + encodeRelations(msg.removedRelations());
+ stream << msg.addedTags();
+ stream << msg.removedTags();
+}
+
+Protocol::ChangeNotification ChangeRecorderPrivate::loadCollectionNotification(QDataStream &stream, quint64 version) const
+{
+ QByteArray resource, destinationResource;
+ int operation, entityCnt;
+ quint64 uid, parentCollection, parentDestCollection;
+ QString remoteId, remoteRevision, dummyString;
+ QSet<QByteArray> changedParts, dummyBa;
+ QSet<qint64> dummyIv;
+
+ Protocol::CollectionChangeNotification msg;
+
+ if (version == 1) {
+ stream >> operation;
+ stream >> uid;
+ stream >> remoteId;
+ stream >> resource;
+ stream >> parentCollection;
+ stream >> parentDestCollection;
+ stream >> dummyString;
+ stream >> changedParts;
+
+ msg.setId(uid);
+ msg.setRemoteId(remoteId);
+ } else if (version >= 2) {
+ stream >> operation;
+ stream >> entityCnt;
+ for (int j = 0; j < entityCnt; ++j) {
+ stream >> uid;
+ stream >> remoteId;
+ stream >> remoteRevision;
+ stream >> dummyString;
+ msg.setId(uid);
+ msg.setRemoteId(remoteId);
+ msg.setRemoteRevision(remoteRevision);
+ }
+ stream >> resource;
+ stream >> destinationResource;
+ stream >> parentCollection;
+ stream >> parentDestCollection;
+ stream >> changedParts;
+ stream >> dummyBa;
+ stream >> dummyBa;
+ if (version >= 3) {
+ stream >> dummyIv;
+ stream >> dummyIv;
+ }
+ }
+ if (version >= 5) {
+ msg.setOperation(static_cast<Protocol::CollectionChangeNotification::Operation>(operation));
+ } else {
+ msg.setOperation(mapCollectionOperation(static_cast<LegacyOp>(operation)));
+ }
+ msg.setResource(resource);
+ msg.setDestinationResource(destinationResource);
+ msg.setParentCollection(parentCollection);
+ msg.setParentDestCollection(parentDestCollection);
+ msg.setChangedParts(changedParts);
+ return msg;
+}
+
+void Akonadi::ChangeRecorderPrivate::saveCollectionNotification(QDataStream& stream, const Protocol::CollectionChangeNotification &msg)
+{
+ stream << int(msg.operation());
+ stream << int(1);
+ stream << msg.id();
+ stream << msg.remoteId();
+ stream << msg.remoteRevision();
+ stream << QString();
+ stream << msg.resource();
+ stream << msg.destinationResource();
+ stream << quint64(msg.parentCollection());
+ stream << quint64(msg.parentDestCollection());
+ stream << msg.changedParts();
+ stream << QSet<QByteArray>();
+ stream << QSet<QByteArray>();
+ stream << QSet<qint64>();
+ stream << QSet<qint64>();
+}
+
+Protocol::ChangeNotification ChangeRecorderPrivate::loadTagNotification(QDataStream &stream, quint64 version) const
+{
+ QByteArray resource, dummyBa;
+ int operation, entityCnt;
+ quint64 uid, dummyI;
+ QString remoteId, dummyString;
+ QSet<QByteArray> dummyBaV;
+ QSet<qint64> dummyIv;
+
+ Protocol::TagChangeNotification msg;
+
+ if (version == 1) {
+ stream >> operation;
+ stream >> uid;
+ stream >> remoteId;
+ stream >> dummyBa;
+ stream >> dummyI;
+ stream >> dummyI;
+ stream >> dummyString;
+ stream >> dummyBaV;
+
+ msg.setId(uid);
+ msg.setRemoteId(remoteId);
+ } else if (version >= 2) {
+ stream >> operation;
+ stream >> entityCnt;
+ for (int j = 0; j < entityCnt; ++j) {
+ stream >> uid;
+ stream >> remoteId;
+ stream >> dummyString;
+ stream >> dummyString;
+ msg.setId(uid);
+ msg.setRemoteId(remoteId);
+ }
+ stream >> resource;
+ stream >> dummyBa;
+ stream >> dummyI;
+ stream >> dummyI;
+ stream >> dummyBaV;
+ stream >> dummyBaV;
+ stream >> dummyBaV;
+ if (version >= 3) {
+ stream >> dummyIv;
+ stream >> dummyIv;
+ }
+ }
+ if (version >= 5) {
+ msg.setOperation(static_cast<Protocol::TagChangeNotification::Operation>(operation));
+ } else {
+ msg.setOperation(mapTagOperation(static_cast<LegacyOp>(operation)));
+ }
+ msg.setResource(resource);
+ return msg;
+}
+
+void Akonadi::ChangeRecorderPrivate::saveTagNotification(QDataStream& stream, const Protocol::TagChangeNotification &msg)
+{
+ stream << int(msg.operation());
+ stream << int(1);
+ stream << msg.id();
+ stream << msg.remoteId();
+ stream << QString();
+ stream << QString();
+ stream << msg.resource();
+ stream << qint64(0);
+ stream << qint64(0);
+ stream << qint64(0);
+ stream << QSet<QByteArray>();
+ stream << QSet<QByteArray>();
+ stream << QSet<QByteArray>();
+ stream << QSet<qint64>();
+ stream << QSet<qint64>();
+}
+
+Protocol::ChangeNotification ChangeRecorderPrivate::loadRelationNotification(QDataStream &stream, quint64 version) const
+{
+ QByteArray dummyBa;
+ int operation, entityCnt;
+ quint64 dummyI;
+ QString dummyString;
+ QSet<QByteArray> itemParts, dummyBaV;
+ QSet<qint64> dummyIv;
+
+ Protocol::RelationChangeNotification msg;
+
+ if (version == 1) {
+ qWarning() << "Invalid version of relation notification";
+ return msg;
+ } else if (version >= 2) {
+ stream >> operation;
+ stream >> entityCnt;
+ for (int j = 0; j < entityCnt; ++j) {
+ stream >> dummyI;
+ stream >> dummyString;
+ stream >> dummyString;
+ stream >> dummyString;
+ }
+ stream >> dummyBa;
+ stream >> dummyBa;
+ stream >> dummyI;
+ stream >> dummyI;
+ stream >> itemParts;
+ stream >> dummyBaV;
+ stream >> dummyBaV;
+ if (version >= 3) {
+ stream >> dummyIv;
+ stream >> dummyIv;
+ }
+ }
+
+ if (version >= 5) {
+ msg.setOperation(static_cast<Protocol::RelationChangeNotification::Operation>(operation));
+ } else {
+ msg.setOperation(mapRelationOperation(static_cast<LegacyOp>(operation)));
+ }
+ Q_FOREACH (const QByteArray &part, itemParts) {
+ const QByteArrayList p = part.split(' ');
+ if (p.size() < 2) {
+ continue;
+ }
+ if (p[0] == "LEFT") {
+ msg.setLeftItem(p[1].toLongLong());
+ } else if (p[0] == "RIGHT") {
+ msg.setRightItem(p[1].toLongLong());
+ } else if (p[0] == "RID") {
+ msg.setRemoteId(QString::fromLatin1(p[1]));
+ } else if (p[0] == "TYPE") {
+ msg.setType(QString::fromLatin1(p[1]));
+ }
+ }
+ return msg;
+}
+
+void Akonadi::ChangeRecorderPrivate::saveRelationNotification(QDataStream& stream, const Protocol::RelationChangeNotification &msg)
+{
+ QSet<QByteArray> rv;
+ rv.insert("LEFT " + QByteArray::number(msg.leftItem()));
+ rv.insert("RIGHT " + QByteArray::number(msg.rightItem()));
+ rv.insert("RID " + msg.remoteId().toLatin1());
+ rv.insert("TYPE " + msg.type().toLatin1());
+
+ stream << int(msg.operation());
+ stream << int(0);
+ stream << qint64(0);
+ stream << QString();
+ stream << QString();
+ stream << QString();
+ stream << QByteArray();
+ stream << qint64(0);
+ stream << qint64(0);
+ stream << qint64(0);
+ stream << rv;
+ stream << QSet<QByteArray>();
+ stream << QSet<QByteArray>();
+ stream << QSet<qint64>();
+ stream << QSet<qint64>();
+}
+
+Protocol::ItemChangeNotification::Operation ChangeRecorderPrivate::mapItemOperation(LegacyOp op) const
+{
+ switch (op) {
+ case Add:
+ return Protocol::ItemChangeNotification::Add;
+ case Modify:
+ return Protocol::ItemChangeNotification::Modify;
+ case Move:
+ return Protocol::ItemChangeNotification::Move;
+ case Remove:
+ return Protocol::ItemChangeNotification::Remove;
+ case Link:
+ return Protocol::ItemChangeNotification::Link;
+ case Unlink:
+ return Protocol::ItemChangeNotification::Unlink;
+ case ModifyFlags:
+ return Protocol::ItemChangeNotification::ModifyFlags;
+ case ModifyTags:
+ return Protocol::ItemChangeNotification::ModifyTags;
+ case ModifyRelations:
+ return Protocol::ItemChangeNotification::ModifyRelations;
+ default:
+ qWarning() << "Unexpected operation type in item notification";
+ return Protocol::ItemChangeNotification::InvalidOp;
+ }
+}
+
+Protocol::CollectionChangeNotification::Operation ChangeRecorderPrivate::mapCollectionOperation(LegacyOp op) const
+{
+ switch (op) {
+ case Add:
+ return Protocol::CollectionChangeNotification::Add;
+ case Modify:
+ return Protocol::CollectionChangeNotification::Modify;
+ case Move:
+ return Protocol::CollectionChangeNotification::Move;
+ case Remove:
+ return Protocol::CollectionChangeNotification::Remove;
+ case Subscribe:
+ return Protocol::CollectionChangeNotification::Subscribe;
+ case Unsubscribe:
+ return Protocol::CollectionChangeNotification::Unsubscribe;
+ default:
+ qWarning() << "Unexpected operation type in collection notification";
+ return Protocol::CollectionChangeNotification::InvalidOp;
+ }
+}
+
+Protocol::TagChangeNotification::Operation ChangeRecorderPrivate::mapTagOperation(LegacyOp op) const
+{
+ switch (op) {
+ case Add:
+ return Protocol::TagChangeNotification::Add;
+ case Modify:
+ return Protocol::TagChangeNotification::Modify;
+ case Remove:
+ return Protocol::TagChangeNotification::Remove;
+ default:
+ qWarning() << "Unexpected operation type in tag notification";
+ return Protocol::TagChangeNotification::InvalidOp;
+ }
+}
+
+Protocol::RelationChangeNotification::Operation ChangeRecorderPrivate::mapRelationOperation(LegacyOp op) const
+{
+ switch (op) {
+ case Add:
+ return Protocol::RelationChangeNotification::Add;
+ case Remove:
+ return Protocol::RelationChangeNotification::Remove;
+ default:
+ qWarning() << "Unexpected operation type in relation notification";
+ return Protocol::RelationChangeNotification::InvalidOp;
+ }
+}
+
+ChangeRecorderPrivate::LegacyType ChangeRecorderPrivate::mapToLegacyType(Protocol::Command::Type type) const
+{
+ switch (type) {
+ case Protocol::Command::ItemChangeNotification:
+ return Item;
+ case Protocol::Command::CollectionChangeNotification:
+ return Collection;
+ case Protocol::Command::TagChangeNotification:
+ return Tag;
+ case Protocol::Command::RelationChangeNotification:
+ return Relation;
+ default:
+ qWarning() << "Unexpected notification type";
+ return InvalidType;
+ }
+}
diff --git a/src/core/changerecorder_p.h b/src/core/changerecorder_p.h
index e6c3010..a979fc1 100644
--- a/src/core/changerecorder_p.h
+++ b/src/core/changerecorder_p.h
@@ -24,6 +24,8 @@
#include "changerecorder.h"
#include "monitor_p.h"
+class QDataStream;
+
namespace Akonadi
{
@@ -55,6 +57,48 @@ public:
void saveNotifications();
void saveTo(QIODevice *device);
private:
+ enum LegacyType {
+ InvalidType,
+ Item,
+ Collection,
+ Tag,
+ Relation
+ };
+ enum LegacyOp {
+ InvalidOp,
+ Add,
+ Modify,
+ Move,
+ Remove,
+ Link,
+ Unlink,
+ Subscribe,
+ Unsubscribe,
+ ModifyFlags,
+ ModifyTags,
+ ModifyRelations
+ };
+
+ Protocol::ChangeNotification loadItemNotification(QSettings *settings) const;
+ Protocol::ChangeNotification loadCollectionNotification(QSettings *settings) const;
+ Protocol::ChangeNotification loadItemNotification(QDataStream &stream, quint64 version) const;
+ Protocol::ChangeNotification loadCollectionNotification(QDataStream &stream, quint64 version) const;
+ Protocol::ChangeNotification loadTagNotification(QDataStream &stream, quint64 version) const;
+ Protocol::ChangeNotification loadRelationNotification(QDataStream &stream, quint64 version) const;
+ void saveItemNotification(QDataStream &stream, const Protocol::ItemChangeNotification &ntf);
+ void saveCollectionNotification(QDataStream &stream, const Protocol::CollectionChangeNotification &ntf);
+ void saveTagNotification(QDataStream &stream, const Protocol::TagChangeNotification &ntf);
+ void saveRelationNotification(QDataStream &stream, const Protocol::RelationChangeNotification &ntf);
+
+ Protocol::ItemChangeNotification::Operation mapItemOperation(LegacyOp op) const;
+ Protocol::CollectionChangeNotification::Operation mapCollectionOperation(LegacyOp op) const;
+ Protocol::TagChangeNotification::Operation mapTagOperation(LegacyOp op) const;
+ Protocol::RelationChangeNotification::Operation mapRelationOperation(LegacyOp op) const;
+ LegacyType mapToLegacyType(Protocol::Command::Type type) const;
+
+ QSet<Protocol::ItemChangeNotification::Relation> extractRelations(QSet<QByteArray> &flags) const;
+ QSet<QByteArray> encodeRelations(const QSet<Protocol::ItemChangeNotification::Relation> &relations) const;
+
void dequeueNotification();
void notificationsLoaded();
void writeStartOffset();
diff --git a/src/core/connectionthread.cpp b/src/core/connection.cpp
index baaff78..17db780 100644
--- a/src/core/connectionthread.cpp
+++ b/src/core/connection.cpp
@@ -17,7 +17,7 @@
* 02110-1301, USA.
*/
-#include "connectionthread_p.h"
+#include "connection_p.h"
#include "session_p.h"
#include "servermanager_p.h"
#include "akonadicore_debug.h"
@@ -37,8 +37,9 @@
using namespace Akonadi;
-ConnectionThread::ConnectionThread(const QByteArray &sessionId, QObject *parent)
+Connection::Connection(ConnectionType connType, const QByteArray &sessionId, QObject *parent)
: QObject(parent)
+ , mConnectionType(connType)
, mSocket(Q_NULLPTR)
, mLogFile(Q_NULLPTR)
, mSessionId(sessionId)
@@ -46,15 +47,12 @@ ConnectionThread::ConnectionThread(const QByteArray &sessionId, QObject *parent)
qRegisterMetaType<Protocol::Command>();
qRegisterMetaType<QAbstractSocket::SocketState>();
- QThread *thread = new QThread();
- moveToThread(thread);
- thread->start();
-
const QByteArray sessionLogFile = qgetenv("AKONADI_SESSION_LOGFILE");
if (!sessionLogFile.isEmpty()) {
- mLogFile = new QFile(QStringLiteral("%1.%2.%3").arg(QString::fromLatin1(sessionLogFile),
- QString::number(QApplication::applicationPid()),
- QString::fromLatin1(mSessionId.replace('/', '_'))));
+ mLogFile = new QFile(QStringLiteral("%1.%2.%3-%4").arg(QString::fromLatin1(sessionLogFile))
+ .arg(QString::number(QApplication::applicationPid()))
+ .arg(QString::fromLatin1(mSessionId.replace('/', '_')))
+ .arg(connType == CommandConnection ? QStringLiteral("Cmd") : QStringLiteral("Ntf")));
if (!mLogFile->open(QIODevice::WriteOnly | QIODevice::Truncate)) {
qCWarning(AKONADICORE_LOG) << "Failed to open Akonadi Session log file" << mLogFile->fileName();
delete mLogFile;
@@ -63,44 +61,24 @@ ConnectionThread::ConnectionThread(const QByteArray &sessionId, QObject *parent)
}
}
-ConnectionThread::~ConnectionThread()
+Connection::~Connection()
{
- if (QCoreApplication::instance()) {
- QMetaObject::invokeMethod(this, "doThreadQuit");
- } else {
- // QCoreApplication already destroyed -> invokeMethod would just not get the message delivered
- // We leak the socket, but at least we don't block for 10s
- qWarning() << "Akonadi ConnectionThread deleted after QCoreApplication is destroyed. Clean up your sessions earlier!";
- thread()->quit();
- }
- if (!thread()->wait(10 * 1000)) {
- thread()->terminate();
- // Make sure to wait until it's done, otherwise it can crash when the pthread callback is called
- thread()->wait();
- }
delete mLogFile;
- delete thread();
-}
-
-void ConnectionThread::doThreadQuit()
-{
- Q_ASSERT(QThread::currentThread() == thread());
if (mSocket) {
mSocket->disconnect(this);
mSocket->close();
delete mSocket;
}
- thread()->quit();
}
-void ConnectionThread::reconnect()
+void Connection::reconnect()
{
const bool ok = QMetaObject::invokeMethod(this, "doReconnect", Qt::QueuedConnection);
Q_ASSERT(ok);
Q_UNUSED(ok)
}
-void ConnectionThread::doReconnect()
+void Connection::doReconnect()
{
Q_ASSERT(QThread::currentThread() == thread());
@@ -146,9 +124,15 @@ void ConnectionThread::doReconnect()
<< XdgBaseDirs::systemPathList("config");
}
const QSettings connectionSettings(connectionConfigFile, QSettings::IniFormat);
-
const QString defaultSocketDir = StandardDirs::saveDir("data");
- serverAddress = connectionSettings.value(QStringLiteral("Data/UnixPath"), QString(defaultSocketDir + QStringLiteral("/akonadiserver.socket"))).toString();
+
+ if (mConnectionType == CommandConnection) {
+ const QString defaultSocketPath = defaultSocketDir % QStringLiteral("akonadiserver-cmd.socket");
+ serverAddress = connectionSettings.value(QStringLiteral("Data/UnixPath"), defaultSocketPath).toString();
+ } else if (mConnectionType == NotificationConnection) {
+ const QString defaultSocketPath = defaultSocketDir % QStringLiteral("akonadiserver-ntf.socket");
+ serverAddress = connectionSettings.value(QStringLiteral("Notifications/UnixPath"), defaultSocketPath).toString();
+ }
}
// create sockets if not yet done, note that this does not yet allow changing socket types on the fly
@@ -157,11 +141,12 @@ void ConnectionThread::doReconnect()
mSocket = new QLocalSocket(this);
connect(mSocket, static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error), this,
[this](QLocalSocket::LocalSocketError) {
+ qCWarning(AKONADICORE_LOG) << mSocket->errorString();
Q_EMIT socketError(mSocket->errorString());
Q_EMIT socketDisconnected();
});
- connect(mSocket, &QLocalSocket::disconnected, this, &ConnectionThread::socketDisconnected);
- connect(mSocket, &QLocalSocket::readyRead, this, &ConnectionThread::dataReceived);
+ connect(mSocket, &QLocalSocket::disconnected, this, &Connection::socketDisconnected);
+ connect(mSocket, &QLocalSocket::readyRead, this, &Connection::dataReceived);
}
// actually do connect
@@ -171,7 +156,7 @@ void ConnectionThread::doReconnect()
Q_EMIT reconnected();
}
-void ConnectionThread::forceReconnect()
+void Connection::forceReconnect()
{
const bool ok = QMetaObject::invokeMethod(this, "doForceReconnect",
Qt::QueuedConnection);
@@ -179,7 +164,7 @@ void ConnectionThread::forceReconnect()
Q_UNUSED(ok)
}
-void ConnectionThread::doForceReconnect()
+void Connection::doForceReconnect()
{
Q_ASSERT(QThread::currentThread() == thread());
@@ -191,14 +176,14 @@ void ConnectionThread::doForceReconnect()
mSocket = Q_NULLPTR;
}
-void ConnectionThread::disconnect()
+void Connection::closeConnection()
{
- const bool ok = QMetaObject::invokeMethod(this, "doDisconnect", Qt::QueuedConnection);
+ const bool ok = QMetaObject::invokeMethod(this, "doCloseConnection", Qt::QueuedConnection);
Q_ASSERT(ok);
Q_UNUSED(ok)
}
-void ConnectionThread::doDisconnect()
+void Connection::doCloseConnection()
{
Q_ASSERT(QThread::currentThread() == thread());
@@ -208,13 +193,12 @@ void ConnectionThread::doDisconnect()
}
}
-void ConnectionThread::dataReceived()
+void Connection::dataReceived()
{
Q_ASSERT(QThread::currentThread() == thread());
QElapsedTimer timer;
timer.start();
-
while (mSocket->bytesAvailable() > 0) {
QDataStream stream(mSocket);
qint64 tag;
@@ -263,13 +247,13 @@ void ConnectionThread::dataReceived()
// to the jobs through event loop. That will be overall slower but should
// result in much more responsive applications.
if (timer.elapsed() > 50) {
- QThread::currentThread()->eventDispatcher()->processEvents(QEventLoop::ExcludeSocketNotifiers);
+ thread()->eventDispatcher()->processEvents(QEventLoop::ExcludeSocketNotifiers);
timer.restart();
}
}
}
-void ConnectionThread::sendCommand(qint64 tag, const Protocol::Command &cmd)
+void Connection::sendCommand(qint64 tag, const Protocol::Command &cmd)
{
const bool ok = QMetaObject::invokeMethod(this, "doSendCommand",
Qt::QueuedConnection,
@@ -279,7 +263,7 @@ void ConnectionThread::sendCommand(qint64 tag, const Protocol::Command &cmd)
Q_UNUSED(ok)
}
-void ConnectionThread::doSendCommand(qint64 tag, const Protocol::Command &cmd)
+void Connection::doSendCommand(qint64 tag, const Protocol::Command &cmd)
{
Q_ASSERT(QThread::currentThread() == thread());
diff --git a/src/core/connectionthread_p.h b/src/core/connection_p.h
index 3f221a1..9724a20 100644
--- a/src/core/connectionthread_p.h
+++ b/src/core/connection_p.h
@@ -27,23 +27,33 @@
#include <private/protocol_p.h>
+#include "akonadicore_export.h"
+
class QAbstractSocket;
class QFile;
namespace Akonadi
{
-class ConnectionThread : public QObject
+class SessionThread;
+
+class AKONADICORE_EXPORT Connection : public QObject
{
Q_OBJECT
public:
- explicit ConnectionThread(const QByteArray &sessionId, QObject *parent = Q_NULLPTR);
- ~ConnectionThread();
+ enum ConnectionType {
+ CommandConnection,
+ NotificationConnection
+ };
+ Q_ENUMS(ConnectionType)
+
+ explicit Connection(ConnectionType connType, const QByteArray &sessionId, QObject *parent = Q_NULLPTR);
+ ~Connection();
Q_INVOKABLE void reconnect();
void forceReconnect();
- void disconnect();
+ void closeConnection();
void sendCommand(qint64 tag, const Protocol::Command &command);
Q_SIGNALS:
@@ -54,10 +64,9 @@ Q_SIGNALS:
void socketError(const QString &message);
private Q_SLOTS:
- void doThreadQuit();
void doReconnect();
void doForceReconnect();
- void doDisconnect();
+ void doCloseConnection();
void doSendCommand(qint64 tag, const Akonadi::Protocol::Command &command);
void dataReceived();
@@ -66,6 +75,7 @@ private:
bool handleCommand(qint64 tag, const Protocol::Command &cmd);
+ ConnectionType mConnectionType;
QLocalSocket *mSocket;
QFile *mLogFile;
QByteArray mSessionId;
@@ -75,6 +85,8 @@ private:
Protocol::Command cmd;
};
QQueue<Command> mOutQueue;
+
+ friend class Akonadi::SessionThread;
};
}
diff --git a/src/core/jobs/collectionmovejob.cpp b/src/core/jobs/collectionmovejob.cpp
index 41423c7..61f3e4b 100644
--- a/src/core/jobs/collectionmovejob.cpp
+++ b/src/core/jobs/collectionmovejob.cpp
@@ -18,6 +18,7 @@
*/
#include "collectionmovejob.h"
+#include "changemediator_p.h"
#include "collection.h"
#include "job_p.h"
#include "protocolhelper_p.h"
@@ -72,6 +73,8 @@ void CollectionMoveJob::doStart()
const Scope destScope = ProtocolHelper::entitySetToScope(Collection::List() << d->destination);
d->sendCommand(Protocol::MoveCollectionCommand(colScope, destScope));
+
+ ChangeMediator::invalidateCollection(d->collection);
}
bool CollectionMoveJob::doHandleResponse(qint64 tag, const Protocol::Command &response)
diff --git a/src/core/monitor.cpp b/src/core/monitor.cpp
index 258cbef..035f8e2 100644
--- a/src/core/monitor.cpp
+++ b/src/core/monitor.cpp
@@ -41,6 +41,8 @@ Monitor::Monitor(QObject *parent)
{
d_ptr->init();
d_ptr->connectToNotificationManager();
+
+ ChangeMediator::registerMonitor(this);
}
//@cond PRIVATE
@@ -67,15 +69,12 @@ void Monitor::setCollectionMonitored(const Collection &collection, bool monitore
Q_D(Monitor);
if (!d->collections.contains(collection) && monitored) {
d->collections << collection;
- if (d->notificationSource) {
- d->notificationSource->setMonitoredCollection(collection.id(), true);
- }
+ d->pendingModification.startMonitoringCollection(collection.id());
+ d->scheduleSubscriptionUpdate();
} else if (!monitored) {
if (d->collections.removeAll(collection)) {
- d->cleanOldNotifications();
- if (d->notificationSource) {
- d->notificationSource->setMonitoredCollection(collection.id(), false);
- }
+ d->pendingModification.stopMonitoringCollection(collection.id());
+ d->scheduleSubscriptionUpdate();
}
}
@@ -87,15 +86,12 @@ void Monitor::setItemMonitored(const Item &item, bool monitored)
Q_D(Monitor);
if (!d->items.contains(item.id()) && monitored) {
d->items.insert(item.id());
- if (d->notificationSource) {
- d->notificationSource->setMonitoredItem(item.id(), true);
- }
+ d->pendingModification.startMonitoringItem(item.id());
+ d->scheduleSubscriptionUpdate();
} else if (!monitored) {
if (d->items.remove(item.id())) {
- d->cleanOldNotifications();
- if (d->notificationSource) {
- d->notificationSource->setMonitoredItem(item.id(), false);
- }
+ d->pendingModification.stopMonitoringItem(item.id());
+ d->scheduleSubscriptionUpdate();
}
}
@@ -107,15 +103,12 @@ void Monitor::setResourceMonitored(const QByteArray &resource, bool monitored)
Q_D(Monitor);
if (!d->resources.contains(resource) && monitored) {
d->resources.insert(resource);
- if (d->notificationSource) {
- d->notificationSource->setMonitoredResource(resource, true);
- }
+ d->pendingModification.startMonitoringResource(resource);
+ d->scheduleSubscriptionUpdate();
} else if (!monitored) {
if (d->resources.remove(resource)) {
- d->cleanOldNotifications();
- if (d->notificationSource) {
- d->notificationSource->setMonitoredResource(resource, false);
- }
+ d->pendingModification.stopMonitoringResource(resource);
+ d->scheduleSubscriptionUpdate();
}
}
@@ -127,15 +120,12 @@ void Monitor::setMimeTypeMonitored(const QString &mimetype, bool monitored)
Q_D(Monitor);
if (!d->mimetypes.contains(mimetype) && monitored) {
d->mimetypes.insert(mimetype);
- if (d->notificationSource) {
- d->notificationSource->setMonitoredMimeType(mimetype, true);
- }
+ d->pendingModification.startMonitoringMimeType(mimetype);
+ d->scheduleSubscriptionUpdate();
} else if (!monitored) {
if (d->mimetypes.remove(mimetype)) {
- d->cleanOldNotifications();
- if (d->notificationSource) {
- d->notificationSource->setMonitoredMimeType(mimetype, false);
- }
+ d->pendingModification.stopMonitoringMimeType(mimetype);
+ d->scheduleSubscriptionUpdate();
}
}
@@ -147,15 +137,12 @@ void Monitor::setTagMonitored(const Akonadi::Tag &tag, bool monitored)
Q_D(Monitor);
if (!d->tags.contains(tag.id()) && monitored) {
d->tags.insert(tag.id());
- if (d->notificationSource) {
- d->notificationSource->setMonitoredTag(tag.id(), true);
- }
+ d->pendingModification.startMonitoringTag(tag.id());
+ d->scheduleSubscriptionUpdate();
} else if (!monitored) {
if (d->tags.remove(tag.id())) {
- d->cleanOldNotifications();
- if (d->notificationSource) {
- d->notificationSource->setMonitoredTag(tag.id(), false);
- }
+ d->pendingModification.stopMonitoringTag(tag.id());
+ d->scheduleSubscriptionUpdate();
}
}
@@ -167,15 +154,12 @@ void Monitor::setTypeMonitored(Monitor::Type type, bool monitored)
Q_D(Monitor);
if (!d->types.contains(type) && monitored) {
d->types.insert(type);
- if (d->notificationSource) {
- d->notificationSource->setMonitoredType(static_cast<Protocol::ChangeNotification::Type>(type), true);
- }
+ d->pendingModification.startMonitoringType(static_cast<Protocol::ModifySubscriptionCommand::ChangeType>(type));
+ d->scheduleSubscriptionUpdate();
} else if (!monitored) {
if (d->types.remove(type)) {
- d->cleanOldNotifications();
- if (d->notificationSource) {
- d->notificationSource->setMonitoredType(static_cast<Protocol::ChangeNotification::Type>(type), false);
- }
+ d->pendingModification.stopMonitoringType(static_cast<Protocol::ModifySubscriptionCommand::ChangeType>(type));
+ d->scheduleSubscriptionUpdate();
}
}
@@ -191,13 +175,8 @@ void Akonadi::Monitor::setAllMonitored(bool monitored)
d->monitorAll = monitored;
- if (!monitored) {
- d->cleanOldNotifications();
- }
-
- if (d->notificationSource) {
- d->notificationSource->setAllMonitored(monitored);
- }
+ d->pendingModification.setAllMonitored(monitored);
+ d->scheduleSubscriptionUpdate();
emit allMonitored(monitored);
}
@@ -206,9 +185,8 @@ void Monitor::setExclusive(bool exclusive)
{
Q_D(Monitor);
d->exclusive = exclusive;
- if (d->notificationSource) {
- d->notificationSource->setExclusive(exclusive);
- }
+ d->pendingModification.setExclusive(exclusive);
+ d->scheduleSubscriptionUpdate();
}
bool Monitor::exclusive() const
@@ -224,9 +202,8 @@ void Monitor::ignoreSession(Session *session)
if (!d->sessions.contains(session->sessionId())) {
d->sessions << session->sessionId();
connect(session, SIGNAL(destroyed(QObject*)), this, SLOT(slotSessionDestroyed(QObject*)));
- if (d->notificationSource) {
- d->notificationSource->setIgnoredSession(session->sessionId(), true);
- }
+ d->pendingModification.startIgnoringSession(session->sessionId());
+ d->scheduleSubscriptionUpdate();
}
}
@@ -368,9 +345,9 @@ void Monitor::setSession(Akonadi::Session *session)
d->itemCache->setSession(d->session);
d->collectionCache->setSession(d->session);
- if (d->notificationSource) {
- d->notificationSource->setSession(d->session->sessionId());
- }
+
+ // Reconnect with a new session
+ d->connectToNotificationManager();
}
Session *Monitor::session() const
diff --git a/src/core/monitor.h b/src/core/monitor.h
index a15ec84..19b9143 100644
--- a/src/core/monitor.h
+++ b/src/core/monitor.h
@@ -41,7 +41,7 @@ class TagFetchScope;
namespace Protocol
{
-class ChangeNotification;
+class Command;
}
/**
@@ -733,6 +733,8 @@ Q_SIGNALS:
*/
void typeMonitored(const Akonadi::Monitor::Type type, bool monitored);
+ void monitorReady();
+
protected:
//@cond PRIVATE
friend class EntityTreeModel;
@@ -748,7 +750,8 @@ private:
Q_PRIVATE_SLOT(d_ptr, void slotSessionDestroyed(QObject *))
Q_PRIVATE_SLOT(d_ptr, void slotStatisticsChangedFinished(KJob *))
Q_PRIVATE_SLOT(d_ptr, void slotFlushRecentlyChangedCollections())
- Q_PRIVATE_SLOT(d_ptr, void slotNotify(const Akonadi::Protocol::ChangeNotification &))
+ Q_PRIVATE_SLOT(d_ptr, void slotUpdateSubscription())
+ Q_PRIVATE_SLOT(d_ptr, void commandReceived(qint64 tag, const Akonadi::Protocol::Command &))
Q_PRIVATE_SLOT(d_ptr, void dataAvailable())
Q_PRIVATE_SLOT(d_ptr, void serverStateChanged(Akonadi::ServerManager::State))
Q_PRIVATE_SLOT(d_ptr, void invalidateCollectionCache(qint64))
diff --git a/src/core/monitor_p.cpp b/src/core/monitor_p.cpp
index b20d8d4..7167d41 100644
--- a/src/core/monitor_p.cpp
+++ b/src/core/monitor_p.cpp
@@ -32,14 +32,14 @@
#include "akonadicore_debug.h"
using namespace Akonadi;
+class operation;
static const int PipelineSize = 5;
MonitorPrivate::MonitorPrivate(ChangeNotificationDependenciesFactory *dependenciesFactory_, Monitor *parent)
: q_ptr(parent)
, dependenciesFactory(dependenciesFactory_ ? dependenciesFactory_ : new ChangeNotificationDependenciesFactory)
- , notificationSource(0)
- , notificationBus(0)
+ , ntfConnection(Q_NULLPTR)
, monitorAll(false)
, exclusive(false)
, mFetchChangedOnly(false)
@@ -47,13 +47,13 @@ MonitorPrivate::MonitorPrivate(ChangeNotificationDependenciesFactory *dependenci
, collectionCache(0)
, itemCache(0)
, tagCache(0)
+ , pendingModificationTimer(Q_NULLPTR)
+ , monitorReady(false)
, fetchCollection(false)
, fetchCollectionStatistics(false)
, collectionMoveTranslationEnabled(true)
, useRefCounting(false)
{
- qRegisterMetaType<Akonadi::Protocol::ChangeNotification::Type>();
- qDBusRegisterMetaType<Akonadi::Protocol::ChangeNotification::Type>();
}
MonitorPrivate::~MonitorPrivate()
@@ -86,29 +86,22 @@ void MonitorPrivate::init()
bool MonitorPrivate::connectToNotificationManager()
{
- delete notificationSource;
+ if (ntfConnection) {
+ ntfConnection->deleteLater();
+ ntfConnection = Q_NULLPTR;
+ }
- notificationSource = dependenciesFactory->createNotificationSource(q_ptr);
- if (!notificationSource) {
+ if (!session) {
return false;
}
- notificationSource->setSession(session->sessionId());
-
- if (notificationBus) {
- // HACK: Implementation detail: notificationBus is SessionPrivate subclass,
- // so we cannot delete it directly, but we need to delete the owning
- // Session instead, otherwise it will dereference a deleted d_ptr.
- delete notificationBus->parent();
- }
- notificationBus = dependenciesFactory->createNotificationBus(q_ptr, notificationSource);
- if (!notificationBus) {
- delete notificationSource;
- notificationSource = 0;
+ ntfConnection = dependenciesFactory->createNotificationConnection(session);
+ if (!ntfConnection) {
return false;
}
- QObject::connect(notificationBus, SIGNAL(notify(Akonadi::Protocol::ChangeNotification)),
- q_ptr, SLOT(slotNotify(Akonadi::Protocol::ChangeNotification)));
+ q_ptr->connect(ntfConnection, SIGNAL(commandReceived(qint64,Akonadi::Protocol::Command)),
+ q_ptr, SLOT(commandReceived(qint64,Akonadi::Protocol::Command)));
+ ntfConnection->reconnect();
return true;
}
@@ -116,32 +109,7 @@ bool MonitorPrivate::connectToNotificationManager()
void MonitorPrivate::serverStateChanged(ServerManager::State state)
{
if (state == ServerManager::Running) {
- if (connectToNotificationManager()) {
- notificationSource->setAllMonitored(monitorAll);
- notificationSource->setSession(session->sessionId());
- Q_FOREACH (const Collection &col, collections) {
- notificationSource->setMonitoredCollection(col.id(), true);
- }
- Q_FOREACH (const Item::Id id, items) {
- notificationSource->setMonitoredItem(id, true);
- }
- Q_FOREACH (const QByteArray &resource, resources) {
- notificationSource->setMonitoredResource(resource, true);
- }
- Q_FOREACH (const QByteArray &session, sessions) {
- notificationSource->setIgnoredSession(session, true);
- }
- Q_FOREACH (const QString &mimeType, mimetypes) {
- notificationSource->setMonitoredMimeType(mimeType, true);
- }
- Q_FOREACH (Tag::Id tagId, tags) {
- notificationSource->setMonitoredTag(tagId, true);
- }
- Q_FOREACH (Monitor::Type type, types) {
- notificationSource->setMonitoredType(
- static_cast<Protocol::ChangeNotification::Type>(type), true);
- }
- }
+ connectToNotificationManager();
}
}
@@ -157,7 +125,7 @@ void MonitorPrivate::invalidateItemCache(qint64 id)
void MonitorPrivate::invalidateTagCache(qint64 id)
{
- tagCache->update(QList<Tag::Id>() << id, mTagFetchScope);
+ tagCache->update({ id }, mTagFetchScope);
}
int MonitorPrivate::pipelineSize() const
@@ -165,142 +133,164 @@ int MonitorPrivate::pipelineSize() const
return PipelineSize;
}
+void MonitorPrivate::scheduleSubscriptionUpdate()
+{
+ if (pendingModificationTimer || !monitorReady) {
+ return;
+ }
+
+ pendingModificationTimer = new QTimer();
+ pendingModificationTimer->setSingleShot(true);
+ pendingModificationTimer->setInterval(0);
+ pendingModificationTimer->start();
+ q_ptr->connect(pendingModificationTimer, SIGNAL(timeout()),
+ q_ptr, SLOT(slotUpdateSubscription()));
+}
+
+void MonitorPrivate::slotUpdateSubscription()
+{
+ delete pendingModificationTimer;
+ pendingModificationTimer = Q_NULLPTR;
+
+ if (ntfConnection) {
+ ntfConnection->sendCommand(3, pendingModification);
+ pendingModification = Protocol::ModifySubscriptionCommand();
+ }
+}
+
bool MonitorPrivate::isLazilyIgnored(const Protocol::ChangeNotification &msg, bool allowModifyFlagsConversion) const
{
- Protocol::ChangeNotification::Operation op = msg.operation();
+ if (msg.type() == Protocol::Command::CollectionChangeNotification) {
+ // Lazy fetching can only affects items.
+ return false;
+ }
- if (msg.type() == Protocol::ChangeNotification::Tags
- && ((op == Protocol::ChangeNotification::Add && q_ptr->receivers(SIGNAL(tagAdded(Akonadi::Tag))) == 0)
- || (op == Protocol::ChangeNotification::Modify && q_ptr->receivers(SIGNAL(tagChanged(Akonadi::Tag))) == 0)
- || (op == Protocol::ChangeNotification::Remove && q_ptr->receivers(SIGNAL(tagRemoved(Akonadi::Tag))) == 0))) {
- return true;
+ if (msg.type() == Protocol::Command::TagChangeNotification) {
+ const auto op = static_cast<const Protocol::TagChangeNotification&>(msg).operation();
+ return ((op == Protocol::TagChangeNotification::Add && q_ptr->receivers(SIGNAL(tagAdded(Akonadi::Tag))) == 0)
+ || (op == Protocol::TagChangeNotification::Modify && q_ptr->receivers(SIGNAL(tagChanged(Akonadi::Tag))) == 0)
+ || (op == Protocol::TagChangeNotification::Remove && q_ptr->receivers(SIGNAL(tagRemoved(Akonadi::Tag))) == 0));
}
- if (!fetchCollectionStatistics
- && (msg.type() == Protocol::ChangeNotification::Items)
- && ((op == Protocol::ChangeNotification::Add && q_ptr->receivers(SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection))) == 0)
- || (op == Protocol::ChangeNotification::Remove && q_ptr->receivers(SIGNAL(itemRemoved(Akonadi::Item))) == 0
+ if (!fetchCollectionStatistics && msg.type() == Protocol::Command::ItemChangeNotification) {
+ const auto &itemNtf = static_cast<const Protocol::ItemChangeNotification&>(msg);
+ const auto op = itemNtf.operation();
+ if ((op == Protocol::ItemChangeNotification::Add && q_ptr->receivers(SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection))) == 0)
+ || (op == Protocol::ItemChangeNotification::Remove && q_ptr->receivers(SIGNAL(itemRemoved(Akonadi::Item))) == 0
&& q_ptr->receivers(SIGNAL(itemsRemoved(Akonadi::Item::List))) == 0)
- || (op == Protocol::ChangeNotification::Modify && q_ptr->receivers(SIGNAL(itemChanged(Akonadi::Item,QSet<QByteArray>))) == 0)
- || (op == Protocol::ChangeNotification::ModifyFlags
+ || (op == Protocol::ItemChangeNotification::Modify && q_ptr->receivers(SIGNAL(itemChanged(Akonadi::Item,QSet<QByteArray>))) == 0)
+ || (op == Protocol::ItemChangeNotification::ModifyFlags
&& (q_ptr->receivers(SIGNAL(itemsFlagsChanged(Akonadi::Item::List,QSet<QByteArray>,QSet<QByteArray>))) == 0
// Newly delivered ModifyFlags notifications will be converted to
// itemChanged(item, "FLAGS") for legacy clients.
&& (!allowModifyFlagsConversion || q_ptr->receivers(SIGNAL(itemChanged(Akonadi::Item,QSet<QByteArray>))) == 0)))
- || (op == Protocol::ChangeNotification::ModifyTags && q_ptr->receivers(SIGNAL(itemsTagsChanged(Akonadi::Item::List,QSet<Akonadi::Tag>,QSet<Akonadi::Tag>))) == 0)
- || (op == Protocol::ChangeNotification::Move && q_ptr->receivers(SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection))) == 0
+ || (op == Protocol::ItemChangeNotification::ModifyTags && q_ptr->receivers(SIGNAL(itemsTagsChanged(Akonadi::Item::List,QSet<Akonadi::Tag>,QSet<Akonadi::Tag>))) == 0)
+ || (op == Protocol::ItemChangeNotification::Move && q_ptr->receivers(SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection))) == 0
&& q_ptr->receivers(SIGNAL(itemsMoved(Akonadi::Item::List,Akonadi::Collection,Akonadi::Collection))) == 0)
- || (op == Protocol::ChangeNotification::Link && q_ptr->receivers(SIGNAL(itemLinked(Akonadi::Item,Akonadi::Collection))) == 0
+ || (op == Protocol::ItemChangeNotification::Link && q_ptr->receivers(SIGNAL(itemLinked(Akonadi::Item,Akonadi::Collection))) == 0
&& q_ptr->receivers(SIGNAL(itemsLinked(Akonadi::Item::List,Akonadi::Collection))) == 0)
- || (op == Protocol::ChangeNotification::Unlink && q_ptr->receivers(SIGNAL(itemUnlinked(Akonadi::Item,Akonadi::Collection))) == 0
- && q_ptr->receivers(SIGNAL(itemsUnlinked(Akonadi::Item::List,Akonadi::Collection))) == 0))) {
- return true;
- }
+ || (op == Protocol::ItemChangeNotification::Unlink && q_ptr->receivers(SIGNAL(itemUnlinked(Akonadi::Item,Akonadi::Collection))) == 0
+ && q_ptr->receivers(SIGNAL(itemsUnlinked(Akonadi::Item::List,Akonadi::Collection))) == 0)) {
+ return true;
+ }
- if (!useRefCounting) {
- return false;
- }
+ if (!useRefCounting) {
+ return false;
+ }
- if (msg.type() == Protocol::ChangeNotification::Collections) {
- // Lazy fetching can only affects items.
- return false;
- }
+ const Collection::Id parentCollectionId = itemNtf.parentCollection();
- Collection::Id parentCollectionId = msg.parentCollection();
+ if ((op == Protocol::ItemChangeNotification::Add)
+ || (op == Protocol::ItemChangeNotification::Remove)
+ || (op == Protocol::ItemChangeNotification::Modify)
+ || (op == Protocol::ItemChangeNotification::ModifyFlags)
+ || (op == Protocol::ItemChangeNotification::ModifyTags)
+ || (op == Protocol::ItemChangeNotification::Link)
+ || (op == Protocol::ItemChangeNotification::Unlink)) {
+ if (isMonitored(parentCollectionId)) {
+ return false;
+ }
+ }
- if ((op == Protocol::ChangeNotification::Add)
- || (op == Protocol::ChangeNotification::Remove)
- || (op == Protocol::ChangeNotification::Modify)
- || (op == Protocol::ChangeNotification::ModifyFlags)
- || (op == Protocol::ChangeNotification::ModifyTags)
- || (op == Protocol::ChangeNotification::Link)
- || (op == Protocol::ChangeNotification::Unlink)) {
- if (isMonitored(parentCollectionId)) {
+ if (op == Protocol::ItemChangeNotification::Move) {
+ if (!isMonitored(parentCollectionId) && !isMonitored(itemNtf.parentDestCollection())) {
+ return true;
+ }
+ // We can't ignore the move. It must be transformed later into a removal or insertion.
return false;
}
+ return true;
}
- if (op == Protocol::ChangeNotification::Move) {
- if (!isMonitored(parentCollectionId) && !isMonitored(msg.parentDestCollection())) {
- return true;
- }
- // We can't ignore the move. It must be transformed later into a removal or insertion.
- return false;
- }
- return true;
+ return false;
}
void MonitorPrivate::checkBatchSupport(const Protocol::ChangeNotification &msg, bool &needsSplit, bool &batchSupported) const
{
- const bool isBatch = (msg.entities().count() > 1);
-
- if (msg.type() == Protocol::ChangeNotification::Items) {
- switch (msg.operation()) {
- case Protocol::ChangeNotification::Add:
- needsSplit = isBatch;
- batchSupported = false;
- return;
- case Protocol::ChangeNotification::Modify:
- needsSplit = isBatch;
- batchSupported = false;
- return;
- case Protocol::ChangeNotification::ModifyFlags:
- batchSupported = q_ptr->receivers(SIGNAL(itemsFlagsChanged(Akonadi::Item::List,QSet<QByteArray>,QSet<QByteArray>))) > 0;
- needsSplit = isBatch && !batchSupported && q_ptr->receivers(SIGNAL(itemChanged(Akonadi::Item,QSet<QByteArray>))) > 0;
- return;
- case Protocol::ChangeNotification::ModifyTags:
- // Tags were added after batch notifications, so they are always supported
- batchSupported = true;
- needsSplit = false;
- return;
- case Protocol::ChangeNotification::ModifyRelations:
- // Relations were added after batch notifications, so they are always supported
- batchSupported = true;
- needsSplit = false;
- return;
- case Protocol::ChangeNotification::Move:
- needsSplit = isBatch && q_ptr->receivers(SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection))) > 0;
- batchSupported = q_ptr->receivers(SIGNAL(itemsMoved(Akonadi::Item::List,Akonadi::Collection,Akonadi::Collection))) > 0;
- return;
- case Protocol::ChangeNotification::Remove:
- needsSplit = isBatch && q_ptr->receivers(SIGNAL(itemRemoved(Akonadi::Item))) > 0;
- batchSupported = q_ptr->receivers(SIGNAL(itemsRemoved(Akonadi::Item::List))) > 0;
- return;
- case Protocol::ChangeNotification::Link:
- needsSplit = isBatch && q_ptr->receivers(SIGNAL(itemLinked(Akonadi::Item,Akonadi::Collection))) > 0;
- batchSupported = q_ptr->receivers(SIGNAL(itemsLinked(Akonadi::Item::List,Akonadi::Collection))) > 0;
- return;
- case Protocol::ChangeNotification::Unlink:
- needsSplit = isBatch && q_ptr->receivers(SIGNAL(itemUnlinked(Akonadi::Item,Akonadi::Collection))) > 0;
- batchSupported = q_ptr->receivers(SIGNAL(itemsUnlinked(Akonadi::Item::List,Akonadi::Collection))) > 0;
- return;
- default:
- needsSplit = isBatch;
- batchSupported = false;
- qCDebug(AKONADICORE_LOG) << "Unknown operation type" << msg.operation() << "in item change notification";
- return;
- }
- } else if (msg.type() == Protocol::ChangeNotification::Collections) {
+ if (msg.type() != Protocol::Command::ItemChangeNotification) {
+ needsSplit = false;
+ batchSupported = false;
+ return;
+ }
+
+ const auto itemNtf = static_cast<const Protocol::ItemChangeNotification*>(&msg);
+ const bool isBatch = (itemNtf->items().count() > 1);
+
+ switch (itemNtf->operation()) {
+ case Protocol::ItemChangeNotification::Add:
needsSplit = isBatch;
batchSupported = false;
- } else if (msg.type() == Protocol::ChangeNotification::Tags) {
+ return;
+ case Protocol::ItemChangeNotification::Modify:
needsSplit = isBatch;
batchSupported = false;
- } else if (msg.type() == Protocol::ChangeNotification::Relations) {
+ return;
+ case Protocol::ItemChangeNotification::ModifyFlags:
+ batchSupported = q_ptr->receivers(SIGNAL(itemsFlagsChanged(Akonadi::Item::List,QSet<QByteArray>,QSet<QByteArray>))) > 0;
+ needsSplit = isBatch && !batchSupported && q_ptr->receivers(SIGNAL(itemChanged(Akonadi::Item,QSet<QByteArray>))) > 0;
+ return;
+ case Protocol::ItemChangeNotification::ModifyTags:
+ // Tags were added after batch notifications, so they are always supported
+ batchSupported = true;
+ needsSplit = false;
+ return;
+ case Protocol::ItemChangeNotification::ModifyRelations:
+ // Relations were added after batch notifications, so they are always supported
+ batchSupported = true;
+ needsSplit = false;
+ return;
+ case Protocol::ItemChangeNotification::Move:
+ needsSplit = isBatch && q_ptr->receivers(SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection))) > 0;
+ batchSupported = q_ptr->receivers(SIGNAL(itemsMoved(Akonadi::Item::List,Akonadi::Collection,Akonadi::Collection))) > 0;
+ return;
+ case Protocol::ItemChangeNotification::Remove:
+ needsSplit = isBatch && q_ptr->receivers(SIGNAL(itemRemoved(Akonadi::Item))) > 0;
+ batchSupported = q_ptr->receivers(SIGNAL(itemsRemoved(Akonadi::Item::List))) > 0;
+ return;
+ case Protocol::ItemChangeNotification::Link:
+ needsSplit = isBatch && q_ptr->receivers(SIGNAL(itemLinked(Akonadi::Item,Akonadi::Collection))) > 0;
+ batchSupported = q_ptr->receivers(SIGNAL(itemsLinked(Akonadi::Item::List,Akonadi::Collection))) > 0;
+ return;
+ case Protocol::ItemChangeNotification::Unlink:
+ needsSplit = isBatch && q_ptr->receivers(SIGNAL(itemUnlinked(Akonadi::Item,Akonadi::Collection))) > 0;
+ batchSupported = q_ptr->receivers(SIGNAL(itemsUnlinked(Akonadi::Item::List,Akonadi::Collection))) > 0;
+ return;
+ default:
needsSplit = isBatch;
batchSupported = false;
+ qCDebug(AKONADICORE_LOG) << "Unknown operation type" << itemNtf->operation() << "in item change notification";
+ return;
}
}
-Protocol::ChangeNotification::List MonitorPrivate::splitMessage(const Protocol::ChangeNotification &msg, bool legacy) const
+Protocol::ChangeNotification::List MonitorPrivate::splitMessage(const Protocol::ItemChangeNotification &msg, bool legacy) const
{
Protocol::ChangeNotification::List list;
- Protocol::ChangeNotification baseMsg;
+ Protocol::ItemChangeNotification baseMsg;
baseMsg.setSessionId(msg.sessionId());
- baseMsg.setType(msg.type());
- if (legacy && msg.operation() == Protocol::ChangeNotification::ModifyFlags) {
- baseMsg.setOperation(Protocol::ChangeNotification::Modify);
+ if (legacy && msg.operation() == Protocol::ItemChangeNotification::ModifyFlags) {
+ baseMsg.setOperation(Protocol::ItemChangeNotification::Modify);
baseMsg.setItemParts(QSet<QByteArray>() << "FLAGS");
} else {
baseMsg.setOperation(msg.operation());
@@ -315,131 +305,17 @@ Protocol::ChangeNotification::List MonitorPrivate::splitMessage(const Protocol::
baseMsg.setAddedTags(msg.addedTags());
baseMsg.setRemovedTags(msg.removedTags());
- list.reserve(msg.entities().count());
- Q_FOREACH (const Protocol::ChangeNotification::Entity &entity, msg.entities()) {
- Protocol::ChangeNotification copy = baseMsg;
- copy.addEntity(entity.id, entity.remoteId, entity.remoteRevision, entity.mimeType);
-
+ const auto items = msg.items();
+ list.reserve(items.count());
+ Q_FOREACH (const Protocol::ItemChangeNotification::Item &item, items) {
+ Protocol::ItemChangeNotification copy = baseMsg;
+ copy.addItem(item.id, item.remoteId, item.remoteRevision, item.mimeType);
list << copy;
}
return list;
}
-bool MonitorPrivate::acceptNotification(const Akonadi::Protocol::ChangeNotification &msg) const
-{
- // session is ignored
- if (sessions.contains(msg.sessionId())) {
- return false;
- }
-
- if (msg.entities().count() == 0 && msg.type() != Protocol::ChangeNotification::Relations) {
- return false;
- }
-
- // user requested everything
- if (monitorAll && msg.type() != Protocol::ChangeNotification::InvalidType) {
- return true;
- }
-
- // Types are monitored, but not this one
- if (!types.isEmpty() && !types.contains(static_cast<Monitor::Type>(msg.type()))) {
- return false;
- }
-
- switch (msg.type()) {
- case Protocol::ChangeNotification::InvalidType:
- qCWarning(AKONADICORE_LOG) << "Received invalid change notification!";
- return false;
-
- case Protocol::ChangeNotification::Items:
- // we have a resource or mimetype filter
- if (!resources.isEmpty() || !mimetypes.isEmpty()) {
- if (resources.contains(msg.resource()) || isMoveDestinationResourceMonitored(msg)) {
- return true;
- }
-
- Q_FOREACH (const Protocol::ChangeNotification::Entity &entity, msg.entities()) {
- if (isMimeTypeMonitored(entity.mimeType)) {
- return true;
- }
- }
- return false;
- }
-
- // we explicitly monitor that item or the collections it's in
- Q_FOREACH (const Protocol::ChangeNotification::Entity &entity, msg.entities()) {
- if (items.contains(entity.id)) {
- return true;
- }
- }
-
- return isCollectionMonitored(msg.parentCollection())
- || isCollectionMonitored(msg.parentDestCollection());
-
- case Protocol::ChangeNotification::Collections:
- // we have a resource filter
- if (!resources.isEmpty()) {
- const bool resourceMatches = resources.contains(msg.resource()) || isMoveDestinationResourceMonitored(msg);
- // a bit hacky, but match the behaviour from the item case,
- // if resource is the only thing we are filtering on, stop here, and if the resource filter matched, of course
- if (mimetypes.isEmpty() || resourceMatches) {
- return resourceMatches;
- }
- // else continue
- }
-
- // we explicitly monitor that colleciton, or all of them
- Q_FOREACH (const Protocol::ChangeNotification::Entity &entity, msg.entities()) {
- if (isCollectionMonitored(entity.id)) {
- return true;
- }
- }
- return isCollectionMonitored(msg.parentCollection())
- || isCollectionMonitored(msg.parentDestCollection());
-
- case Protocol::ChangeNotification::Tags:
- if (!tags.isEmpty()) {
- Q_FOREACH (const Protocol::ChangeNotification::Entity &entity, msg.entities()) {
- if (tags.contains(entity.id)) {
- return true;
- }
- }
- return false;
- }
- return true;
- case Protocol::ChangeNotification::Relations:
- return true;
- }
- Q_ASSERT(false);
- return false;
-}
-
-void MonitorPrivate::cleanOldNotifications()
-{
- bool erased = false;
- for (QQueue<Protocol::ChangeNotification>::iterator it = pipeline.begin(); it != pipeline.end();) {
- if (!acceptNotification(*it) || isLazilyIgnored(*it)) {
- it = pipeline.erase(it);
- erased = true;
- } else {
- ++it;
- }
- }
-
- for (QQueue<Protocol::ChangeNotification>::iterator it = pendingNotifications.begin(); it != pendingNotifications.end();) {
- if (!acceptNotification(*it) || isLazilyIgnored(*it)) {
- it = pendingNotifications.erase(it);
- erased = true;
- } else {
- ++it;
- }
- }
- if (erased) {
- notificationsErased();
- }
-}
-
bool MonitorPrivate::fetchCollections() const
{
return fetchCollection;
@@ -452,39 +328,52 @@ bool MonitorPrivate::fetchItems() const
bool MonitorPrivate::ensureDataAvailable(const Protocol::ChangeNotification &msg)
{
- if (msg.type() == Protocol::ChangeNotification::Tags) {
- Q_FOREACH (const Protocol::ChangeNotification::Entity &entity, msg.entities()) {
- if (!tagCache->ensureCached(QList<Tag::Id>() << entity.id, mTagFetchScope)) {
- return false;
- }
- }
- return true;
+ bool allCached = true;
+ if (msg.isRemove()) {
+ return allCached; // the actual object is gone already, nothing to fetch there
+ }
+
+ if (msg.type() == Protocol::Command::TagChangeNotification) {
+ return tagCache->ensureCached({ static_cast<const Protocol::TagChangeNotification&>(msg).id() }, mTagFetchScope);
}
- if (msg.type() == Protocol::ChangeNotification::Relations) {
+ if (msg.type() == Protocol::Command::RelationChangeNotification) {
return true;
}
- if (msg.operation() == Protocol::ChangeNotification::Remove && msg.type() == Protocol::ChangeNotification::Collections) {
+ if (msg.type() == Protocol::Command::CollectionChangeNotification
+ && static_cast<const Protocol::CollectionChangeNotification&>(msg).operation() == Protocol::CollectionChangeNotification::Remove) {
//For collection removals the collection is gone anyways, so we can't fetch it. Rid will be set later on instead.
return true;
}
- bool allCached = true;
if (fetchCollections()) {
- if (!collectionCache->ensureCached(msg.parentCollection(), mCollectionFetchScope)) {
+ const qint64 parentCollection = (msg.type() == Protocol::Command::ItemChangeNotification) ?
+ static_cast<const Protocol::ItemChangeNotification&>(msg).parentCollection() :
+ (msg.type() == Protocol::Command::CollectionChangeNotification) ?
+ static_cast<const Protocol::CollectionChangeNotification&>(msg).parentCollection() :
+ -1;
+ if (parentCollection > -1 && !collectionCache->ensureCached(parentCollection, mCollectionFetchScope)) {
allCached = false;
}
- if (msg.operation() == Protocol::ChangeNotification::Move && !collectionCache->ensureCached(msg.parentDestCollection(), mCollectionFetchScope)) {
+
+ qint64 parentDestCollection = -1;
+
+ if ((msg.type() == Protocol::Command::ItemChangeNotification)
+ && (static_cast<const Protocol::ItemChangeNotification&>(msg).operation() == Protocol::ItemChangeNotification::Move)) {
+ parentDestCollection = static_cast<const Protocol::ItemChangeNotification&>(msg).parentDestCollection();
+ } else if ((msg.type() == Protocol::Command::CollectionChangeNotification)
+ && (static_cast<const Protocol::CollectionChangeNotification&>(msg).operation() == Protocol::CollectionChangeNotification::Move)) {
+ parentDestCollection = static_cast<const Protocol::CollectionChangeNotification&>(msg).parentDestCollection();
+ }
+ if (parentDestCollection > -1 && !collectionCache->ensureCached(parentDestCollection, mCollectionFetchScope)) {
allCached = false;
}
}
- if (msg.operation() == Protocol::ChangeNotification::Remove) {
- return allCached; // the actual object is gone already, nothing to fetch there
- }
- if (msg.type() == Protocol::ChangeNotification::Items && fetchItems()) {
+ if (msg.type() == Protocol::Command::ItemChangeNotification && fetchItems()) {
ItemFetchScope scope(mItemFetchScope);
- if (mFetchChangedOnly && (msg.operation() == Protocol::ChangeNotification::Modify || msg.operation() == Protocol::ChangeNotification::ModifyFlags)) {
+ const auto &itemNtf = static_cast<const Protocol::ItemChangeNotification&>(msg);
+ if (mFetchChangedOnly && (itemNtf.operation() == Protocol::ItemChangeNotification::Modify || itemNtf.operation() == Protocol::ItemChangeNotification::ModifyFlags)) {
bool fullPayloadWasRequested = scope.fullPayload();
scope.fetchFullPayload(false);
QSet<QByteArray> requestedPayloadParts = scope.payloadParts();
@@ -498,7 +387,7 @@ bool MonitorPrivate::ensureDataAvailable(const Protocol::ChangeNotification &msg
scope.fetchAttribute(part, false);
}
- QSet<QByteArray> changedParts = msg.itemParts();
+ QSet<QByteArray> changedParts = itemNtf.itemParts();
Q_FOREACH (const QByteArray &part, changedParts) {
if (part.startsWith("PLD:") && //krazy:exclude=strings since QByteArray
(fullPayloadWasRequested || requestedPayloadParts.contains(part))) {
@@ -510,88 +399,73 @@ bool MonitorPrivate::ensureDataAvailable(const Protocol::ChangeNotification &msg
}
}
}
- if (!itemCache->ensureCached(msg.uids(), scope)) {
+ if (!itemCache->ensureCached(itemNtf.uids(), scope)) {
allCached = false;
}
// Make sure all tags for ModifyTags operation are in cache too
- if (msg.operation() == Protocol::ChangeNotification::ModifyTags) {
- if (!tagCache->ensureCached((msg.addedTags() + msg.removedTags()).toList(), mTagFetchScope)) {
+ if (itemNtf.operation() == Protocol::ItemChangeNotification::ModifyTags) {
+ if (!tagCache->ensureCached((itemNtf.addedTags() + itemNtf.removedTags()).toList(), mTagFetchScope)) {
allCached = false;
}
}
- } else if (msg.type() == Protocol::ChangeNotification::Collections && fetchCollections()) {
- Q_FOREACH (const Protocol::ChangeNotification::Entity &entity, msg.entities()) {
- if (!collectionCache->ensureCached(entity.id, mCollectionFetchScope)) {
- allCached = false;
- break;
- }
+ } else if (msg.type() == Protocol::Command::CollectionChangeNotification && fetchCollections()) {
+ const qint64 colId = static_cast<const Protocol::CollectionChangeNotification&>(msg).id();
+ if (!collectionCache->ensureCached(colId, mCollectionFetchScope)) {
+ allCached = false;
}
}
+
return allCached;
}
bool MonitorPrivate::emitNotification(const Protocol::ChangeNotification &msg)
{
bool someoneWasListening = false;
- bool shouldCleanOldNotifications = false;
- if (msg.type() == Protocol::ChangeNotification::Tags) {
+ if (msg.type() == Protocol::Command::TagChangeNotification) {
+ const auto &tagNtf = static_cast<const Protocol::TagChangeNotification&>(msg);
//In case of a Remove notification this will return a list of invalid entities (we'll deal later with them)
- const Tag::List tags = tagCache->retrieve(msg.uids());
- someoneWasListening = emitTagsNotification(msg, tags);
- shouldCleanOldNotifications = !someoneWasListening;
- } else if (msg.type() == Protocol::ChangeNotification::Relations) {
+ const Tag::List tags = tagCache->retrieve({ tagNtf.id() });
+ someoneWasListening = emitTagNotification(tagNtf, tags.isEmpty() ? Tag() : tags[0]);
+ } else if (msg.type() == Protocol::Command::RelationChangeNotification) {
+ const auto &relNtf = static_cast<const Protocol::RelationChangeNotification&>(msg);
Relation rel;
- Q_FOREACH (const QByteArray &part, msg.itemParts()) {
- QList<QByteArray> splitPart = part.split(' ');
- Q_ASSERT(splitPart.size() == 2);
- if (splitPart.first() == "LEFT") {
- rel.setLeft(Akonadi::Item(splitPart.at(1).toLongLong()));
- } else if (splitPart.first() == "RIGHT") {
- rel.setRight(Akonadi::Item(splitPart.at(1).toLongLong()));
- } else if (splitPart.first() == "TYPE") {
- rel.setType(splitPart.at(1));
- } else if (splitPart.first() == "RID") {
- rel.setRemoteId(splitPart.at(1));
- }
- }
- someoneWasListening = emitRelationsNotification(msg, Relation::List() << rel);
- shouldCleanOldNotifications = !someoneWasListening;
- } else {
- const Collection parent = collectionCache->retrieve(msg.parentCollection());
+ rel.setLeft(Akonadi::Item(relNtf.leftItem()));
+ rel.setRight(Akonadi::Item(relNtf.rightItem()));
+ rel.setType(relNtf.type().toLatin1());
+ rel.setRemoteId(relNtf.remoteId().toLatin1());
+ someoneWasListening = emitRelationNotification(relNtf, rel);
+ } else if (msg.type() == Protocol::Command::CollectionChangeNotification) {
+ const auto &colNtf = static_cast<const Protocol::CollectionChangeNotification&>(msg);
+ const Collection parent = collectionCache->retrieve(colNtf.parentCollection());
Collection destParent;
- if (msg.operation() == Protocol::ChangeNotification::Move) {
- destParent = collectionCache->retrieve(msg.parentDestCollection());
- }
-
- if (msg.type() == Protocol::ChangeNotification::Collections) {
- Collection col;
- Q_FOREACH (const Protocol::ChangeNotification::Entity &entity, msg.entities()) {
- //For removals this will retrieve an invalid collection. We'll deal with that in emitCollectionNotification
- col = collectionCache->retrieve(entity.id);
- //It is possible that the retrieval fails also in the non-removal case (e.g. because the item was meanwhile removed while
- //the changerecorder stored the notification or the notification was in the queue). In order to drop such invalid notifications we have to ignore them.
- if (col.isValid() || msg.operation() == Protocol::ChangeNotification::Remove || !fetchCollections()) {
- someoneWasListening = emitCollectionNotification(msg, col, parent, destParent);
- shouldCleanOldNotifications = !someoneWasListening;
- }
- }
- } else if (msg.type() == Protocol::ChangeNotification::Items) {
- //For removals this will retrieve an empty set. We'll deal with that in emitItemNotification
- const Item::List items = itemCache->retrieve(msg.uids());
- //It is possible that the retrieval fails also in the non-removal case (e.g. because the item was meanwhile removed while
- //the changerecorder stored the notification or the notification was in the queue). In order to drop such invalid notifications we have to ignore them.
- if (!items.isEmpty() || msg.operation() == Protocol::ChangeNotification::Remove || !fetchItems()) {
- someoneWasListening = emitItemsNotification(msg, items, parent, destParent);
- shouldCleanOldNotifications = !someoneWasListening;
- }
+ if (colNtf.operation() == Protocol::CollectionChangeNotification::Move) {
+ destParent = collectionCache->retrieve(colNtf.parentDestCollection());
}
- }
- if (shouldCleanOldNotifications) {
- cleanOldNotifications(); // probably someone disconnected a signal in the meantime, get rid of the no longer interesting stuff
+ //For removals this will retrieve an invalid collection. We'll deal with that in emitCollectionNotification
+ const Collection col = collectionCache->retrieve(colNtf.id());
+ //It is possible that the retrieval fails also in the non-removal case (e.g. because the item was meanwhile removed while
+ //the changerecorder stored the notification or the notification was in the queue). In order to drop such invalid notifications we have to ignore them.
+ if (col.isValid() || colNtf.operation() == Protocol::CollectionChangeNotification::Remove || !fetchCollections()) {
+ someoneWasListening = emitCollectionNotification(msg, col, parent, destParent);
+ }
+ } else if (msg.type() == Protocol::Command::ItemChangeNotification) {
+ const auto &itemNtf = static_cast<const Protocol::ItemChangeNotification&>(msg);
+ const Collection parent = collectionCache->retrieve(itemNtf.parentCollection());
+ Collection destParent;
+ if (itemNtf.operation() == Protocol::ItemChangeNotification::Move) {
+ destParent = collectionCache->retrieve(itemNtf.parentDestCollection());
+ }
+ //For removals this will retrieve an empty set. We'll deal with that in emitItemNotification
+ const Item::List items = itemCache->retrieve(itemNtf.uids());
+ //It is possible that the retrieval fails also in the non-removal case (e.g. because the item was meanwhile removed while
+ //the changerecorder stored the notification or the notification was in the queue). In order to drop such invalid notifications we have to ignore them.
+ if (!items.isEmpty() || itemNtf.operation() == Protocol::ItemChangeNotification::Remove || !fetchItems()) {
+ someoneWasListening = emitItemsNotification(msg, items, parent, destParent);
+ }
}
return someoneWasListening;
@@ -599,14 +473,16 @@ bool MonitorPrivate::emitNotification(const Protocol::ChangeNotification &msg)
void MonitorPrivate::updatePendingStatistics(const Protocol::ChangeNotification &msg)
{
- if (msg.type() == Protocol::ChangeNotification::Items) {
- notifyCollectionStatisticsWatchers(msg.parentCollection(), msg.resource());
+ if (msg.type() == Protocol::Command::ItemChangeNotification) {
+ const auto &itemNtf = static_cast<const Protocol::ItemChangeNotification&>(msg);
+ notifyCollectionStatisticsWatchers(itemNtf.parentCollection(), itemNtf.resource());
// FIXME use the proper resource of the target collection, for cross resource moves
- notifyCollectionStatisticsWatchers(msg.parentDestCollection(), msg.destinationResource());
- } else if (msg.type() == Protocol::ChangeNotification::Collections && msg.operation() == Protocol::ChangeNotification::Remove) {
- // no need for statistics updates anymore
- Q_FOREACH (const Protocol::ChangeNotification::Entity &entity, msg.entities()) {
- recentlyChangedCollections.remove(entity.id);
+ notifyCollectionStatisticsWatchers(itemNtf.parentDestCollection(), itemNtf.destinationResource());
+ } else if (msg.type() == Protocol::Command::CollectionChangeNotification) {
+ const auto &colNtf = static_cast<const Protocol::CollectionChangeNotification&>(msg);
+ if (colNtf.operation() == Protocol::CollectionChangeNotification::Remove) {
+ // no need for statistics updates anymore
+ recentlyChangedCollections.remove(colNtf.id());
}
}
}
@@ -616,9 +492,8 @@ void MonitorPrivate::slotSessionDestroyed(QObject *object)
Session *objectSession = qobject_cast<Session *>(object);
if (objectSession) {
sessions.removeAll(objectSession->sessionId());
- if (notificationSource) {
- notificationSource->setIgnoredSession(objectSession->sessionId(), false);
- }
+ pendingModification.stopIgnoringSession(objectSession->sessionId());
+ scheduleSubscriptionUpdate();
}
}
@@ -648,69 +523,163 @@ void MonitorPrivate::slotFlushRecentlyChangedCollections()
recentlyChangedCollections.clear();
}
-int MonitorPrivate::translateAndCompress(QQueue< Protocol::ChangeNotification > &notificationQueue, const Protocol::ChangeNotification &msg)
+int MonitorPrivate::translateAndCompress(QQueue<Protocol::ChangeNotification> &notificationQueue, const Protocol::ChangeNotification &msg)
{
- // We have to split moves into insert or remove if the source or destination
- // is not monitored.
- if (msg.operation() != Protocol::ChangeNotification::Move) {
+ // Always handle tags and relations
+ if (msg.type() == Protocol::Command::TagChangeNotification
+ || msg.type() == Protocol::Command::RelationChangeNotification) {
notificationQueue.enqueue(msg);
return 1;
}
- // Always handle tags
- if (msg.type() == Protocol::ChangeNotification::Tags) {
+ // We have to split moves into insert or remove if the source or destination
+ // is not monitored.
+ if (!msg.isMove()) {
notificationQueue.enqueue(msg);
return 1;
}
+
bool sourceWatched = false;
bool destWatched = false;
- if (useRefCounting && msg.type() == Protocol::ChangeNotification::Items) {
- sourceWatched = isMonitored(msg.parentCollection());
- destWatched = isMonitored(msg.parentDestCollection());
- } else {
+ if (msg.type() == Protocol::Command::ItemChangeNotification) {
+ const auto &itemNtf = static_cast<const Protocol::ItemChangeNotification&>(msg);
+ if (useRefCounting) {
+ sourceWatched = isMonitored(itemNtf.parentCollection());
+ destWatched = isMonitored(itemNtf.parentDestCollection());
+ } else {
+ if (!resources.isEmpty()) {
+ sourceWatched = resources.contains(itemNtf.resource());
+ destWatched = isMoveDestinationResourceMonitored(itemNtf);
+ }
+ if (!sourceWatched) {
+ sourceWatched = isCollectionMonitored(itemNtf.parentCollection());
+ }
+ if (!destWatched) {
+ destWatched = isCollectionMonitored(itemNtf.parentDestCollection());
+ }
+ }
+ } else if (msg.type() == Protocol::Command::CollectionChangeNotification) {
+ const auto &colNtf = static_cast<const Protocol::CollectionChangeNotification&>(msg);
if (!resources.isEmpty()) {
- sourceWatched = resources.contains(msg.resource());
- destWatched = isMoveDestinationResourceMonitored(msg);
+ sourceWatched = resources.contains(colNtf.resource());
+ destWatched = isMoveDestinationResourceMonitored(colNtf);
}
if (!sourceWatched) {
- sourceWatched = isCollectionMonitored(msg.parentCollection());
+ sourceWatched = isCollectionMonitored(colNtf.parentCollection());
}
if (!destWatched) {
- destWatched = isCollectionMonitored(msg.parentDestCollection());
+ destWatched = isCollectionMonitored(colNtf.parentDestCollection());
}
+ } else {
+ Q_ASSERT(false);
+ return 0;
}
if (!sourceWatched && !destWatched) {
return 0;
}
- if ((sourceWatched && destWatched) || (!collectionMoveTranslationEnabled && msg.type() == Protocol::ChangeNotification::Collections)) {
+ if ((sourceWatched && destWatched) || (!collectionMoveTranslationEnabled && msg.type() == Protocol::Command::CollectionChangeNotification)) {
notificationQueue.enqueue(msg);
return 1;
}
if (sourceWatched) {
- // Transform into a removal
- Protocol::ChangeNotification removalMessage = msg;
- removalMessage.setOperation(Protocol::ChangeNotification::Remove);
- removalMessage.setParentDestCollection(-1);
- notificationQueue.enqueue(removalMessage);
- return 1;
+ if (msg.type() == Protocol::Command::ItemChangeNotification) {
+ Protocol::ItemChangeNotification removalMessage = msg;
+ removalMessage.setOperation(Protocol::ItemChangeNotification::Remove);
+ removalMessage.setParentDestCollection(-1);
+ notificationQueue.enqueue(removalMessage);
+ return 1;
+ } else {
+ Protocol::CollectionChangeNotification removalMessage = msg;
+ removalMessage.setOperation(Protocol::CollectionChangeNotification::Remove);
+ removalMessage.setParentDestCollection(-1);
+ notificationQueue.enqueue(removalMessage);
+ return 1;
+ }
}
// Transform into an insertion
- Protocol::ChangeNotification insertionMessage = msg;
- insertionMessage.setOperation(Protocol::ChangeNotification::Add);
- insertionMessage.setParentCollection(msg.parentDestCollection());
- insertionMessage.setParentDestCollection(-1);
- // We don't support batch insertion, so we have to do it one by one
- const Protocol::ChangeNotification::List split = splitMessage(insertionMessage, false);
- Q_FOREACH (const Protocol::ChangeNotification &insertion, split) {
- notificationQueue.enqueue(insertion);
- }
- return split.count();
+ if (msg.type() == Protocol::Command::ItemChangeNotification) {
+ Protocol::ItemChangeNotification insertionMessage = msg;
+ insertionMessage.setOperation(Protocol::ItemChangeNotification::Add);
+ insertionMessage.setParentCollection(insertionMessage.parentDestCollection());
+ insertionMessage.setParentDestCollection(-1);
+ // We don't support batch insertion, so we have to do it one by one
+ const auto split = splitMessage(insertionMessage, false);
+ Q_FOREACH (const Protocol::ChangeNotification &insertion, split) {
+ notificationQueue.enqueue(insertion);
+ }
+ return split.count();
+ } else if (msg.type() == Protocol::Command::CollectionChangeNotification) {
+ Protocol::CollectionChangeNotification insertionMessage = msg;
+ insertionMessage.setOperation(Protocol::CollectionChangeNotification::Add);
+ insertionMessage.setParentCollection(insertionMessage.parentDestCollection());
+ insertionMessage.setParentDestCollection(-1);
+ notificationQueue.enqueue(insertionMessage);
+ return 1;
+ }
+
+ Q_ASSERT(false);
+ return 0;
+}
+
+void MonitorPrivate::commandReceived(qint64 tag, const Protocol::Command &command)
+{
+ Q_Q(Monitor);
+ Q_UNUSED(tag);
+ if (command.isResponse()) {
+ switch (command.type()) {
+ case Protocol::Command::Hello: {
+ Protocol::HelloResponse hello(command);
+ qCDebug(AKONADICORE_LOG) << q_ptr << "Connected to notification bus";
+ QByteArray subname = session->sessionId() + " - ";
+ if (!q->objectName().isEmpty()) {
+ subname += q->objectName().toLatin1();
+ } else {
+ subname += QByteArray::number(quintptr(q));
+ }
+ qCDebug(AKONADICORE_LOG) << q_ptr << "Subscribing as \"" << subname << "\"";
+ Protocol::CreateSubscriptionCommand subCmd(subname, session->sessionId());
+ ntfConnection->sendCommand(2, subCmd);
+ break;
+ }
+
+ case Protocol::Command::CreateSubscription: {
+ Protocol::ModifySubscriptionCommand msubCmd = pendingModification;
+ pendingModification = Protocol::ModifySubscriptionCommand();
+ ntfConnection->sendCommand(3, msubCmd);
+ break;
+ }
+
+ case Protocol::Command::ModifySubscription:
+ // TODO: Handle errors
+ if (!monitorReady) {
+ monitorReady = true;
+ Q_EMIT q_ptr->monitorReady();
+ }
+ break;
+
+ default:
+ qCWarning(AKONADICORE_LOG) << "Received an unexpected response on Notification stream: "<< command.debugString();
+ break;
+ }
+ } else {
+ switch (command.type()) {
+ case Protocol::Command::ItemChangeNotification:
+ case Protocol::Command::CollectionChangeNotification:
+ case Protocol::Command::TagChangeNotification:
+ case Protocol::Command::RelationChangeNotification:
+ slotNotify(command);
+ break;
+ default:
+ qCWarning(AKONADICORE_LOG) << "Received an unexpected message on Notification stream:" << command.debugString();
+ break;
+ }
+ }
}
/*
@@ -739,9 +708,11 @@ void MonitorPrivate::slotNotify(const Protocol::ChangeNotification &msg)
checkBatchSupport(msg, needsSplit, supportsBatch);
+ const bool isModifyFlags = (msg.type() == Protocol::Command::ItemChangeNotification
+ && static_cast<const Protocol::ItemChangeNotification&>(msg).operation() == Protocol::ItemChangeNotification::ModifyFlags);
if (supportsBatch
- || (!needsSplit && !supportsBatch && msg.operation() != Protocol::ChangeNotification::ModifyFlags)
- || msg.type() == Protocol::ChangeNotification::Collections) {
+ || (!needsSplit && !supportsBatch && !isModifyFlags)
+ || msg.type() == Protocol::Command::CollectionChangeNotification) {
// Make sure the batch msg is always queued before the split notifications
const int oldSize = pendingNotifications.size();
const int appended = translateAndCompress(pendingNotifications, msg);
@@ -759,7 +730,9 @@ void MonitorPrivate::slotNotify(const Protocol::ChangeNotification &msg)
} else if (needsSplit) {
// If it's not queued at least make sure we fetch all the items from split
// notifications in one go.
- itemCache->ensureCached(msg.uids(), mItemFetchScope);
+ if (msg.type() == Protocol::Command::ItemChangeNotification) {
+ itemCache->ensureCached(static_cast<const Protocol::ItemChangeNotification&>(msg).uids(), mItemFetchScope);
+ }
}
// if the message contains more items, but we need to emit single-item notification,
@@ -767,10 +740,12 @@ void MonitorPrivate::slotNotify(const Protocol::ChangeNotification &msg)
// if the message contains only one item, but batches are not supported
// (and thus neither is flagsModified), splitMessage() will convert the
// notification to regular Modify with "FLAGS" part changed
- if (needsSplit || (!needsSplit && !supportsBatch && msg.operation() == Akonadi::Protocol::ChangeNotification::ModifyFlags)) {
+ if (needsSplit || (!needsSplit && !supportsBatch && isModifyFlags)) {
// Make sure inter-resource move notifications are translated into
// Add/Remove notifications
- if (msg.operation() == Protocol::ChangeNotification::Move && msg.resource() != msg.destinationResource()) {
+ if (msg.type() == Protocol::Command::ItemChangeNotification
+ && static_cast<const Protocol::ItemChangeNotification&>(msg).type() == Protocol::Command::MoveItems
+ && static_cast<const Protocol::ItemChangeNotification&>(msg).resource() != static_cast<const Protocol::ItemChangeNotification&>(msg).destinationResource()) {
if (needsSplit) {
const Protocol::ChangeNotification::List split = splitMessage(msg, !supportsBatch);
Q_FOREACH (const auto &splitMsg, split) {
@@ -832,24 +807,23 @@ void MonitorPrivate::dispatchNotifications()
}
}
-static Relation::List extractRelations(QSet<QByteArray> &flags)
+static Relation::List extractRelations(const QSet<Protocol::ItemChangeNotification::Relation> &rels)
{
Relation::List relations;
- Q_FOREACH (const QByteArray &flag, flags) {
- if (flag.startsWith("RELATION")) {
- flags.remove(flag);
- const QList<QByteArray> parts = flag.split(' ');
- Q_ASSERT(parts.size() == 4);
- relations << Relation(parts[1], Item(parts[2].toLongLong()), Item(parts[3].toLongLong()));
- }
+ if (rels.isEmpty()) {
+ return relations;
+ }
+
+ relations.reserve(rels.size());
+ Q_FOREACH (const auto &rel, rels) {
+ relations.push_back(Relation(rel.type.toLatin1(), Akonadi::Item(rel.leftId), Akonadi::Item(rel.rightId)));
}
return relations;
}
-bool MonitorPrivate::emitItemsNotification(const Protocol::ChangeNotification &msg_, const Item::List &items, const Collection &collection, const Collection &collectionDest)
+bool MonitorPrivate::emitItemsNotification(const Protocol::ItemChangeNotification &msg_, const Item::List &items, const Collection &collection, const Collection &collectionDest)
{
- Protocol::ChangeNotification msg = msg_;
- Q_ASSERT(msg.type() == Protocol::ChangeNotification::Items);
+ Protocol::ItemChangeNotification msg = msg_;
Collection col = collection;
Collection colDest = collectionDest;
if (!col.isValid()) {
@@ -864,38 +838,36 @@ bool MonitorPrivate::emitItemsNotification(const Protocol::ChangeNotification &m
}
}
- Relation::List addedRelations, removedRelations;
- if (msg.operation() == Protocol::ChangeNotification::ModifyRelations) {
- QSet<QByteArray> addedFlags = msg.addedFlags();
- addedRelations = extractRelations(addedFlags);
- msg.setAddedFlags(addedFlags);
+ const QSet<QByteArray> addedFlags = msg.addedFlags();
+ const QSet<QByteArray> removedFlags = msg.removedFlags();
- QSet<QByteArray> removedFlags = msg.removedFlags();
- removedRelations = extractRelations(removedFlags);
- msg.setRemovedFlags(removedFlags);
+ Relation::List addedRelations, removedRelations;
+ if (msg.operation() == Protocol::ItemChangeNotification::ModifyRelations) {
+ addedRelations = extractRelations(msg.addedRelations());
+ removedRelations = extractRelations(msg.removedRelations());
}
Tag::List addedTags, removedTags;
- if (msg.operation() == Protocol::ChangeNotification::ModifyTags) {
+ if (msg.operation() == Protocol::ItemChangeNotification::ModifyTags) {
addedTags = tagCache->retrieve(msg.addedTags().toList());
removedTags = tagCache->retrieve(msg.removedTags().toList());
}
- QMap<Protocol::ChangeNotification::Id, Protocol::ChangeNotification::Entity> msgEntities = msg.entities();
+ QMap<Protocol::ChangeNotification::Id, Protocol::ItemChangeNotification::Item> msgItems = msg.items();
Item::List its = items;
QMutableVectorIterator<Item> iter(its);
while (iter.hasNext()) {
Item it = iter.next();
if (it.isValid()) {
- const Protocol::ChangeNotification::Entity msgEntity = msgEntities[it.id()];
- if (msg.operation() == Protocol::ChangeNotification::Remove) {
- it.setRemoteId(msgEntity.remoteId);
- it.setRemoteRevision(msgEntity.remoteRevision);
- it.setMimeType(msgEntity.mimeType);
+ const Protocol::ItemChangeNotification::Item msgItem = msgItems[it.id()];
+ if (msg.operation() == Protocol::ItemChangeNotification::Remove) {
+ it.setRemoteId(msgItem.remoteId);
+ it.setRemoteRevision(msgItem.remoteRevision);
+ it.setMimeType(msgItem.mimeType);
}
if (!it.parentCollection().isValid()) {
- if (msg.operation() == Protocol::ChangeNotification::Move) {
+ if (msg.operation() == Protocol::ItemChangeNotification::Move) {
it.setParentCollection(colDest);
} else {
it.setParentCollection(col);
@@ -913,27 +885,27 @@ bool MonitorPrivate::emitItemsNotification(const Protocol::ChangeNotification &m
// If one client does a modify followed by a move we have to make sure that the
// AgentBase::itemChanged() in another client always sees the parent collection
// of the item before it has been moved.
- if (msg.operation() != Protocol::ChangeNotification::Move) {
+ if (msg.operation() != Protocol::ItemChangeNotification::Move) {
it.setParentCollection(col);
}
}
}
iter.setValue(it);
- msgEntities.remove(it.id());
+ msgItems.remove(it.id());
} else {
// remove the invalid item
iter.remove();
}
}
- its.reserve(its.size() + msgEntities.size());
+ its.reserve(its.size() + msgItems.size());
// Now reconstruct any items there were left in msgItems
- Q_FOREACH (const Protocol::ChangeNotification::Entity &msgItem, msgEntities) {
+ Q_FOREACH (const Protocol::ItemChangeNotification::Item &msgItem, msgItems) {
Item it(msgItem.id);
it.setRemoteId(msgItem.remoteId);
it.setRemoteRevision(msgItem.remoteRevision);
it.setMimeType(msgItem.mimeType);
- if (msg.operation() == Protocol::ChangeNotification::Move) {
+ if (msg.operation() == Protocol::ItemChangeNotification::Move) {
it.setParentCollection(colDest);
} else {
it.setParentCollection(col);
@@ -943,27 +915,27 @@ bool MonitorPrivate::emitItemsNotification(const Protocol::ChangeNotification &m
bool handled = false;
switch (msg.operation()) {
- case Protocol::ChangeNotification::Add:
+ case Protocol::ItemChangeNotification::Add:
if (q_ptr->receivers(SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection))) > 0) {
Q_ASSERT(its.count() == 1);
emit q_ptr->itemAdded(its.first(), col);
return true;
}
return false;
- case Protocol::ChangeNotification::Modify:
+ case Protocol::ItemChangeNotification::Modify:
if (q_ptr->receivers(SIGNAL(itemChanged(Akonadi::Item,QSet<QByteArray>))) > 0) {
Q_ASSERT(its.count() == 1);
emit q_ptr->itemChanged(its.first(), msg.itemParts());
return true;
}
return false;
- case Protocol::ChangeNotification::ModifyFlags:
+ case Protocol::ItemChangeNotification::ModifyFlags:
if (q_ptr->receivers(SIGNAL(itemsFlagsChanged(Akonadi::Item::List,QSet<QByteArray>,QSet<QByteArray>))) > 0) {
emit q_ptr->itemsFlagsChanged(its, msg.addedFlags(), msg.removedFlags());
handled = true;
}
return handled;
- case Protocol::ChangeNotification::Move:
+ case Protocol::ItemChangeNotification::Move:
if (q_ptr->receivers(SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection))) > 0) {
Q_ASSERT(its.count() == 1);
emit q_ptr->itemMoved(its.first(), col, colDest);
@@ -974,7 +946,7 @@ bool MonitorPrivate::emitItemsNotification(const Protocol::ChangeNotification &m
handled = true;
}
return handled;
- case Protocol::ChangeNotification::Remove:
+ case Protocol::ItemChangeNotification::Remove:
if (q_ptr->receivers(SIGNAL(itemRemoved(Akonadi::Item))) > 0) {
Q_ASSERT(its.count() == 1);
emit q_ptr->itemRemoved(its.first());
@@ -985,7 +957,7 @@ bool MonitorPrivate::emitItemsNotification(const Protocol::ChangeNotification &m
handled = true;
}
return handled;
- case Protocol::ChangeNotification::Link:
+ case Protocol::ItemChangeNotification::Link:
if (q_ptr->receivers(SIGNAL(itemLinked(Akonadi::Item,Akonadi::Collection))) > 0) {
Q_ASSERT(its.count() == 1);
emit q_ptr->itemLinked(its.first(), col);
@@ -996,7 +968,7 @@ bool MonitorPrivate::emitItemsNotification(const Protocol::ChangeNotification &m
handled = true;
}
return handled;
- case Protocol::ChangeNotification::Unlink:
+ case Protocol::ItemChangeNotification::Unlink:
if (q_ptr->receivers(SIGNAL(itemUnlinked(Akonadi::Item,Akonadi::Collection))) > 0) {
Q_ASSERT(its.count() == 1);
emit q_ptr->itemUnlinked(its.first(), col);
@@ -1007,13 +979,13 @@ bool MonitorPrivate::emitItemsNotification(const Protocol::ChangeNotification &m
handled = true;
}
return handled;
- case Protocol::ChangeNotification::ModifyTags:
+ case Protocol::ItemChangeNotification::ModifyTags:
if (q_ptr->receivers(SIGNAL(itemsTagsChanged(Akonadi::Item::List,QSet<Akonadi::Tag>,QSet<Akonadi::Tag>))) > 0) {
emit q_ptr->itemsTagsChanged(its, Akonadi::vectorToSet(addedTags), Akonadi::vectorToSet(removedTags));
return true;
}
return false;
- case Protocol::ChangeNotification::ModifyRelations:
+ case Protocol::ItemChangeNotification::ModifyRelations:
if (q_ptr->receivers(SIGNAL(itemsRelationsChanged(Akonadi::Item::List,Akonadi::Relation::List,Akonadi::Relation::List))) > 0) {
emit q_ptr->itemsRelationsChanged(its, addedRelations, removedRelations);
return true;
@@ -1026,9 +998,8 @@ bool MonitorPrivate::emitItemsNotification(const Protocol::ChangeNotification &m
return false;
}
-bool MonitorPrivate::emitCollectionNotification(const Protocol::ChangeNotification &msg, const Collection &col, const Collection &par, const Collection &dest)
+bool MonitorPrivate::emitCollectionNotification(const Protocol::CollectionChangeNotification &msg, const Collection &col, const Collection &par, const Collection &dest)
{
- Q_ASSERT(msg.type() == Protocol::ChangeNotification::Collections);
Collection parent = par;
if (!parent.isValid()) {
parent = Collection(msg.parentCollection());
@@ -1039,15 +1010,14 @@ bool MonitorPrivate::emitCollectionNotification(const Protocol::ChangeNotificati
}
Collection collection = col;
- Protocol::ChangeNotification::Entity msgEntities = msg.entities().cbegin().value();
- if (!collection.isValid() || msg.operation() == Protocol::ChangeNotification::Remove) {
- collection = Collection(msgEntities.id);
+ if (!collection.isValid() || msg.operation() == Protocol::CollectionChangeNotification::Remove) {
+ collection = Collection(msg.id());
collection.setResource(QString::fromUtf8(msg.resource()));
- collection.setRemoteId(msgEntities.remoteId);
+ collection.setRemoteId(msg.remoteId());
}
if (!collection.parentCollection().isValid()) {
- if (msg.operation() == Protocol::ChangeNotification::Move) {
+ if (msg.operation() == Protocol::CollectionChangeNotification::Move) {
collection.setParentCollection(destination);
} else {
collection.setParentCollection(parent);
@@ -1055,33 +1025,33 @@ bool MonitorPrivate::emitCollectionNotification(const Protocol::ChangeNotificati
}
switch (msg.operation()) {
- case Protocol::ChangeNotification::Add:
+ case Protocol::CollectionChangeNotification::Add:
if (q_ptr->receivers(SIGNAL(collectionAdded(Akonadi::Collection,Akonadi::Collection))) == 0) {
return false;
}
emit q_ptr->collectionAdded(collection, parent);
return true;
- case Protocol::ChangeNotification::Modify:
+ case Protocol::CollectionChangeNotification::Modify:
if (q_ptr->receivers(SIGNAL(collectionChanged(Akonadi::Collection))) == 0
&& q_ptr->receivers(SIGNAL(collectionChanged(Akonadi::Collection,QSet<QByteArray>))) == 0) {
return false;
}
emit q_ptr->collectionChanged(collection);
- emit q_ptr->collectionChanged(collection, msg.itemParts());
+ emit q_ptr->collectionChanged(collection, msg.changedParts());
return true;
- case Protocol::ChangeNotification::Move:
+ case Protocol::CollectionChangeNotification::Move:
if (q_ptr->receivers(SIGNAL(collectionMoved(Akonadi::Collection,Akonadi::Collection,Akonadi::Collection))) == 0) {
return false;
}
emit q_ptr->collectionMoved(collection, parent, destination);
return true;
- case Protocol::ChangeNotification::Remove:
+ case Protocol::CollectionChangeNotification::Remove:
if (q_ptr->receivers(SIGNAL(collectionRemoved(Akonadi::Collection))) == 0) {
return false;
}
emit q_ptr->collectionRemoved(collection);
return true;
- case Protocol::ChangeNotification::Subscribe:
+ case Protocol::CollectionChangeNotification::Subscribe:
if (q_ptr->receivers(SIGNAL(collectionSubscribed(Akonadi::Collection,Akonadi::Collection))) == 0) {
return false;
}
@@ -1089,7 +1059,7 @@ bool MonitorPrivate::emitCollectionNotification(const Protocol::ChangeNotificati
emit q_ptr->collectionSubscribed(collection, parent);
}
return true;
- case Protocol::ChangeNotification::Unsubscribe:
+ case Protocol::CollectionChangeNotification::Unsubscribe:
if (q_ptr->receivers(SIGNAL(collectionUnsubscribed(Akonadi::Collection))) == 0) {
return false;
}
@@ -1104,51 +1074,39 @@ bool MonitorPrivate::emitCollectionNotification(const Protocol::ChangeNotificati
return false;
}
-bool MonitorPrivate::emitTagsNotification(const Protocol::ChangeNotification &msg, const Tag::List &tags)
+bool MonitorPrivate::emitTagNotification(const Protocol::TagChangeNotification &msg, const Tag &tag)
{
- Q_ASSERT(msg.type() == Protocol::ChangeNotification::Tags);
-
- Tag::List validTags;
- if (msg.operation() == Protocol::ChangeNotification::Remove) {
+ Tag validTag;
+ if (msg.operation() == Protocol::TagChangeNotification::Remove) {
//In case of a removed signal the cache entry was already invalidated, and we therefore received an empty list of tags
- validTags.reserve(msg.entities().count());
- Q_FOREACH (const Akonadi::Protocol::ChangeNotification::Entity &entity, msg.entities()) {
- Tag tag(entity.id);
- tag.setRemoteId(entity.remoteId.toLatin1());
- validTags << tag;
- }
+ validTag = Tag(msg.id());
+ validTag.setRemoteId(msg.remoteId().toLatin1());
} else {
- validTags = tags;
+ validTag = tag;
}
- if (validTags.isEmpty()) {
+ if (!validTag.isValid()) {
return false;
}
switch (msg.operation()) {
- case Protocol::ChangeNotification::Add:
+ case Protocol::TagChangeNotification::Add:
if (q_ptr->receivers(SIGNAL(tagAdded(Akonadi::Tag))) == 0) {
return false;
}
- Q_FOREACH (const Tag &tag, validTags) {
- Q_EMIT q_ptr->tagAdded(tag);
- }
+ Q_EMIT q_ptr->tagAdded(validTag);
return true;
- case Protocol::ChangeNotification::Modify:
+ case Protocol::TagChangeNotification::Modify:
if (q_ptr->receivers(SIGNAL(tagChanged(Akonadi::Tag))) == 0) {
return false;
}
- Q_FOREACH (const Tag &tag, validTags) {
- Q_EMIT q_ptr->tagChanged(tag);
- }
+ Q_EMIT q_ptr->tagChanged(validTag);
return true;
- case Protocol::ChangeNotification::Remove:
+ case Protocol::TagChangeNotification::Remove:
if (q_ptr->receivers(SIGNAL(tagRemoved(Akonadi::Tag))) == 0) {
return false;
}
- Q_FOREACH (const Tag &tag, validTags) {
- Q_EMIT q_ptr->tagRemoved(tag);
- }
+ Q_EMIT q_ptr->tagRemoved(validTag);
return true;
default:
qCDebug(AKONADICORE_LOG) << "Unknown operation type" << msg.operation() << "in tag change notification";
@@ -1157,30 +1115,24 @@ bool MonitorPrivate::emitTagsNotification(const Protocol::ChangeNotification &ms
return false;
}
-bool MonitorPrivate::emitRelationsNotification(const Protocol::ChangeNotification &msg, const Relation::List &relations)
+bool MonitorPrivate::emitRelationNotification(const Protocol::RelationChangeNotification &msg, const Relation &relation)
{
- Q_ASSERT(msg.type() == Protocol::ChangeNotification::Relations);
-
- if (relations.isEmpty()) {
+ if (!relation.isValid()) {
return false;
}
switch (msg.operation()) {
- case Protocol::ChangeNotification::Add:
+ case Protocol::RelationChangeNotification::Add:
if (q_ptr->receivers(SIGNAL(relationAdded(Akonadi::Relation))) == 0) {
return false;
}
- Q_FOREACH (const Relation &relation, relations) {
- Q_EMIT q_ptr->relationAdded(relation);
- }
+ Q_EMIT q_ptr->relationAdded(relation);
return true;
- case Protocol::ChangeNotification::Remove:
+ case Protocol::RelationChangeNotification::Remove:
if (q_ptr->receivers(SIGNAL(relationRemoved(Akonadi::Relation))) == 0) {
return false;
}
- Q_FOREACH (const Relation &relation, relations) {
- Q_EMIT q_ptr->relationRemoved(relation);
- }
+ Q_EMIT q_ptr->relationRemoved(relation);
return true;
default:
qCDebug(AKONADICORE_LOG) << "Unknown operation type" << msg.operation() << "in tag change notification";
@@ -1192,33 +1144,44 @@ bool MonitorPrivate::emitRelationsNotification(const Protocol::ChangeNotificatio
void MonitorPrivate::invalidateCaches(const Protocol::ChangeNotification &msg)
{
// remove invalidates
- if (msg.operation() == Protocol::ChangeNotification::Remove) {
- if (msg.type() == Protocol::ChangeNotification::Collections) {
- Q_FOREACH (qint64 uid, msg.uids()) {
- collectionCache->invalidate(uid);
- }
- } else if (msg.type() == Protocol::ChangeNotification::Items) {
- itemCache->invalidate(msg.uids());
- } else if (msg.type() == Protocol::ChangeNotification::Tags) {
- tagCache->invalidate(msg.uids());
- }
- }
-
// modify removes the cache entry, as we need to re-fetch
// And subscription modify the visibility of the collection by the collectionFetchScope.
- if (msg.operation() == Protocol::ChangeNotification::Modify
- || msg.operation() == Protocol::ChangeNotification::ModifyFlags
- || msg.operation() == Protocol::ChangeNotification::ModifyTags
- || msg.operation() == Protocol::ChangeNotification::Move
- || msg.operation() == Protocol::ChangeNotification::Subscribe) {
- if (msg.type() == Protocol::ChangeNotification::Collections) {
- Q_FOREACH (quint64 uid, msg.uids()) {
- collectionCache->update(uid, mCollectionFetchScope);
+ if (msg.isRemove()) {
+ switch (msg.type()) {
+ case Protocol::Command::CollectionChangeNotification: {
+ const auto &colNtf = static_cast<const Protocol::CollectionChangeNotification&>(msg);
+ collectionCache->invalidate(colNtf.id());
+ switch (colNtf.operation()) {
+ case Protocol::CollectionChangeNotification::Modify:
+ case Protocol::CollectionChangeNotification::Move:
+ case Protocol::CollectionChangeNotification::Subscribe:
+ collectionCache->update(colNtf.id(), mCollectionFetchScope);
+ default:
+ break;
+ }
+ } break;
+ case Protocol::Command::ItemChangeNotification: {
+ const auto &itemNtf = static_cast<const Protocol::ItemChangeNotification&>(msg);
+ itemCache->invalidate(itemNtf.uids());
+ switch (itemNtf.operation()) {
+ case Protocol::ItemChangeNotification::Modify:
+ case Protocol::ItemChangeNotification::ModifyFlags:
+ case Protocol::ItemChangeNotification::ModifyTags:
+ case Protocol::ItemChangeNotification::ModifyRelations:
+ itemCache->update(itemNtf.uids(), mItemFetchScope);
+ default:
+ break;
}
- } else if (msg.type() == Protocol::ChangeNotification::Items) {
- itemCache->update(msg.uids(), mItemFetchScope);
- } else if (msg.type() == Protocol::ChangeNotification::Tags) {
- tagCache->update(msg.uids(), mTagFetchScope);
+ } break;
+ case Protocol::Command::TagChangeNotification: {
+ const auto &tagNtf = static_cast<const Protocol::TagChangeNotification&>(msg);
+ tagCache->invalidate({ tagNtf.id() });
+ if (tagNtf.operation() == Protocol::TagChangeNotification::Modify) {
+ tagCache->update({ tagNtf.id() }, mTagFetchScope);
+ }
+ } break;
+ default:
+ break;
}
}
}
diff --git a/src/core/monitor_p.h b/src/core/monitor_p.h
index a94d0f8..ef91796 100644
--- a/src/core/monitor_p.h
+++ b/src/core/monitor_p.h
@@ -32,7 +32,7 @@
#include "entitycache_p.h"
#include "servermanager.h"
#include "changenotificationdependenciesfactory_p.h"
-#include "notificationsource_p.h"
+#include "connection_p.h"
#include "private/protocol_p.h"
@@ -60,8 +60,7 @@ public:
Monitor *q_ptr;
Q_DECLARE_PUBLIC(Monitor)
ChangeNotificationDependenciesFactory *dependenciesFactory;
- NotificationSource *notificationSource;
- QObject *notificationBus;
+ Connection *ntfConnection;
Collection::List collections;
QSet<QByteArray> resources;
QSet<Item::Id> items;
@@ -81,6 +80,10 @@ public:
TagListCache *tagCache;
QMimeDatabase mimeDatabase;
+ Protocol::ModifySubscriptionCommand pendingModification;
+ QTimer *pendingModificationTimer;
+ bool monitorReady;
+
// The waiting list
QQueue<Protocol::ChangeNotification> pendingNotifications;
// The messages for which data is currently being fetched
@@ -105,14 +108,9 @@ public:
// Virtual so it can be overridden in FakeMonitor.
virtual bool connectToNotificationManager();
- bool acceptNotification(const Protocol::ChangeNotification &msg) const;
void dispatchNotifications();
void flushPipeline();
- // Called when the monitored item/collection changes, checks if the queued messages
- // are still accepted, if not they are removed
- void cleanOldNotifications();
-
bool ensureDataAvailable(const Protocol::ChangeNotification &msg);
/**
* Sends out the change notification @p msg.
@@ -142,24 +140,26 @@ public:
*/
int translateAndCompress(QQueue<Protocol::ChangeNotification> &notificationQueue, const Protocol::ChangeNotification &msg);
+ void commandReceived(qint64 tag, const Protocol::Command &command);
+
virtual void slotNotify(const Protocol::ChangeNotification &msg);
/**
* Sends out a change notification for an item.
* @return @c true if the notification was actually send to someone, @c false if no one was listening.
*/
- bool emitItemsNotification(const Protocol::ChangeNotification &msg, const Item::List &items = Item::List(),
+ bool emitItemsNotification(const Protocol::ItemChangeNotification &msg, const Item::List &items = Item::List(),
const Collection &collection = Collection(), const Collection &collectionDest = Collection());
/**
* Sends out a change notification for a collection.
* @return @c true if the notification was actually send to someone, @c false if no one was listening.
*/
- bool emitCollectionNotification(const Protocol::ChangeNotification &msg, const Collection &col = Collection(),
+ bool emitCollectionNotification(const Protocol::CollectionChangeNotification &msg, const Collection &col = Collection(),
const Collection &par = Collection(), const Collection &dest = Collection());
- bool emitTagsNotification(const Protocol::ChangeNotification &msg, const Tag::List &tags);
+ bool emitTagNotification(const Protocol::TagChangeNotification &msg, const Tag &tags);
- bool emitRelationsNotification(const Protocol::ChangeNotification &msg, const Relation::List &relations);
+ bool emitRelationNotification(const Protocol::RelationChangeNotification &msg, const Relation &relation);
void serverStateChanged(Akonadi::ServerManager::State state);
@@ -178,6 +178,9 @@ public:
*/
void invalidateTagCache(qint64 tagId);
+ void scheduleSubscriptionUpdate();
+ void slotUpdateSubscription();
+
/**
@brief Class used to determine when to purge items in a Collection
@@ -255,7 +258,7 @@ private:
*/
void checkBatchSupport(const Protocol::ChangeNotification &msg, bool &needsSplit, bool &batchSupported) const;
- Protocol::ChangeNotification::List splitMessage(const Protocol::ChangeNotification &msg, bool legacy) const;
+ Protocol::ChangeNotification::List splitMessage(const Protocol::ItemChangeNotification &msg, bool legacy) const;
bool isCollectionMonitored(Collection::Id collection) const
{
@@ -291,9 +294,10 @@ private:
return false;
}
- bool isMoveDestinationResourceMonitored(const Protocol::ChangeNotification &msg) const
+ template<typename T>
+ bool isMoveDestinationResourceMonitored(const T &msg) const
{
- if (msg.operation() != Protocol::ChangeNotification::Move) {
+ if (msg.operation() != T::Move) {
return false;
}
return resources.contains(msg.destinationResource());
diff --git a/src/core/notificationbus_p.cpp b/src/core/notificationbus_p.cpp
deleted file mode 100644
index 8b50080..0000000
--- a/src/core/notificationbus_p.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- Copyright (c) 2015 Daniel Vrátil <dvratil@redhat.com>
-
- 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 "notificationbus_p.h"
-#include "session_p.h"
-#include "connectionthread_p.h"
-#include "akonadicore_debug.h"
-
-#include "private/protocol_p.h"
-
-#include <QTimer>
-
-using namespace Akonadi;
-
-NotificationBusPrivate::NotificationBusPrivate(Session *parent)
- : QObject(parent)
- , SessionPrivate(parent)
-{
-}
-
-NotificationBusPrivate::~NotificationBusPrivate()
-{
-}
-
-bool NotificationBusPrivate::handleCommand(qint64 tag, const Protocol::Command &cmd)
-{
- Q_UNUSED(tag);
-
- if (cmd.type() == Protocol::Command::Hello) {
- Protocol::HelloResponse hello(cmd);
- if (hello.isError()) {
- qCWarning(AKONADICORE_LOG) << "Error when establishing connection with Akonadi server:" << hello.errorMessage();
- connThread->disconnect();
- QTimer::singleShot(1000, mParent, SLOT(reconnect()));
- return false;
- }
-
- qCDebug(AKONADICORE_LOG) << "Connected to" << hello.serverName() << ", using protocol version" << hello.protocolVersion();
- qCDebug(AKONADICORE_LOG) << "Server says:" << hello.message();
- // Version mismatch is handled in SessionPrivate::startJob() so that
- // we can report the error out via KJob API
- protocolVersion = hello.protocolVersion();
-
- Protocol::LoginCommand login(sessionId, Protocol::LoginCommand::NotificationBus);
- sendCommand(nextTag(), login);
- return true;
- }
-
- if (cmd.type() == Protocol::Command::Login) {
- Protocol::LoginResponse login(cmd);
- if (login.isError()) {
- qCWarning(AKONADICORE_LOG) << "Unable to login to Akonadi server:" << login.errorMessage();
- connThread->disconnect();
- QTimer::singleShot(1000, mParent, SLOT(reconnect()));
- return false;
- }
-
- connected = true;
- startNext();
- return true;
- }
-
- if (cmd.type() == Protocol::Command::ChangeNotification) {
- Q_EMIT notify(cmd);
- return true;
- }
-
- qCWarning(AKONADICORE_LOG) << "Recieved invalid command on NotificationBus" << sessionId;
- return false;
-}
diff --git a/src/core/session.cpp b/src/core/session.cpp
index bfebdc4..7545acc 100644
--- a/src/core/session.cpp
+++ b/src/core/session.cpp
@@ -25,7 +25,7 @@
#include "servermanager.h"
#include "servermanager_p.h"
#include "protocolhelper_p.h"
-#include "connectionthread_p.h"
+#include "sessionthread_p.h"
#include "private/standarddirs_p.h"
#include "private/protocol_p.h"
@@ -63,21 +63,20 @@ void SessionPrivate::startNext()
void SessionPrivate::reconnect()
{
- if (!connThread) {
- connThread = new ConnectionThread(sessionId);
- mParent->connect(connThread, &ConnectionThread::reconnected, mParent, &Session::reconnected,
- Qt::QueuedConnection);
- mParent->connect(connThread, SIGNAL(commandReceived(qint64,Akonadi::Protocol::Command)),
- mParent, SLOT(handleCommand(qint64, Akonadi::Protocol::Command)),
- Qt::QueuedConnection);
- mParent->connect(connThread, SIGNAL(socketDisconnected()), mParent, SLOT(socketDisconnected()),
- Qt::QueuedConnection);
- mParent->connect(connThread, SIGNAL(socketError(QString)), mParent, SLOT(socketError(QString)),
- Qt::QueuedConnection);
-
+ if (!connection) {
+ connection = sessionThread()->createConnection(Connection::CommandConnection, sessionId);
+ mParent->connect(connection, &Connection::reconnected, mParent, &Session::reconnected,
+ Qt::QueuedConnection);
+ mParent->connect(connection, SIGNAL(commandReceived(qint64,Akonadi::Protocol::Command)),
+ mParent, SLOT(handleCommand(qint64, Akonadi::Protocol::Command)),
+ Qt::QueuedConnection);
+ mParent->connect(connection, SIGNAL(socketDisconnected()), mParent, SLOT(socketDisconnected()),
+ Qt::QueuedConnection);
+ mParent->connect(connection, SIGNAL(socketError(QString)), mParent, SLOT(socketError(QString)),
+ Qt::QueuedConnection);
}
- connThread->reconnect();
+ connection->reconnect();
}
QString SessionPrivate::connectionFile()
@@ -106,8 +105,8 @@ bool SessionPrivate::handleCommand(qint64 tag, const Protocol::Command &cmd)
Protocol::HelloResponse hello(cmd);
if (hello.isError()) {
qCWarning(AKONADICORE_LOG) << "Error when establishing connection with Akonadi server:" << hello.errorMessage();
- connThread->disconnect();
- QTimer::singleShot(1000, connThread, &ConnectionThread::reconnect);
+ connection->closeConnection();
+ QTimer::singleShot(1000, connection, &Connection::reconnect);
return false;
}
@@ -128,7 +127,7 @@ bool SessionPrivate::handleCommand(qint64 tag, const Protocol::Command &cmd)
Protocol::LoginResponse login(cmd);
if (login.isError()) {
qCWarning(AKONADICORE_LOG) << "Unable to login to Akonadi server:" << login.errorMessage();
- connThread->disconnect();
+ connection->closeConnection();
QTimer::singleShot(1000, mParent, SLOT(reconnect()));
return false;
}
@@ -260,7 +259,7 @@ qint64 SessionPrivate::nextTag()
void SessionPrivate::sendCommand(qint64 tag, const Protocol::Command &command)
{
- connThread->sendCommand(tag, command);
+ connection->sendCommand(tag, command);
}
void SessionPrivate::serverStateChanged(ServerManager::State state)
@@ -275,8 +274,8 @@ void SessionPrivate::serverStateChanged(ServerManager::State state)
job->kill(KJob::EmitResult);
}
} else if (state == ServerManager::Stopping) {
- delete connThread;
- connThread = Q_NULLPTR;
+ delete connection;
+ connection = Q_NULLPTR;
}
}
@@ -293,25 +292,25 @@ void SessionPrivate::itemRevisionChanged(Akonadi::Item::Id itemId, int oldRevisi
SessionPrivate::SessionPrivate(Session *parent)
: mParent(parent)
- , thread(0)
- , connThread(0)
+ , mSessionThread(new SessionThread)
+ , connection(Q_NULLPTR)
, protocolVersion(0)
- , currentJob(0)
+ , currentJob(Q_NULLPTR)
{
// Shutdown the thread before QApplication event loop quits - the
- // thread()->wait() mechanism in ConnectionThread dtor crashes sometimes
+ // thread()->wait() mechanism in Connection dtor crashes sometimes
// when called from QApplication destructor
connThreadCleanUp = QObject::connect(qApp, &QCoreApplication::aboutToQuit,
[this]() {
- delete connThread;
- connThread = Q_NULLPTR;
+ delete mSessionThread;
+ mSessionThread = Q_NULLPTR;
});
}
SessionPrivate::~SessionPrivate()
{
QObject::disconnect(connThreadCleanUp);
- delete connThread;
+ delete mSessionThread;
}
void SessionPrivate::init(const QByteArray &id)
@@ -341,8 +340,8 @@ void SessionPrivate::forceReconnect()
{
jobRunning = false;
connected = false;
- if (connThread) {
- connThread->forceReconnect();
+ if (connection) {
+ connection->forceReconnect();
}
QMetaObject::invokeMethod(mParent, "reconnect", Qt::QueuedConnection);
}
diff --git a/src/core/session_p.h b/src/core/session_p.h
index 1be448e..a55ad79 100644
--- a/src/core/session_p.h
+++ b/src/core/session_p.h
@@ -36,7 +36,8 @@ class QIODevice;
namespace Akonadi
{
-class ConnectionThread;
+class SessionThread;
+class Connection;
namespace Protocol
{
@@ -55,6 +56,8 @@ public:
virtual void init(const QByteArray &sessionId);
+ SessionThread *sessionThread() const { return mSessionThread; }
+
void startNext();
/// Disconnects a previously existing connection and tries to reconnect
void forceReconnect();
@@ -124,8 +127,8 @@ public:
static QString connectionFile();
Session *mParent;
- QThread *thread;
- ConnectionThread *connThread;
+ SessionThread *mSessionThread;
+ Connection *connection;
QMetaObject::Connection connThreadCleanUp;
QByteArray sessionId;
bool connected;
diff --git a/src/core/sessionthread.cpp b/src/core/sessionthread.cpp
new file mode 100644
index 0000000..d544fce
--- /dev/null
+++ b/src/core/sessionthread.cpp
@@ -0,0 +1,87 @@
+/*
+ Copyright (c) 2016 Daniel Vrátil <dvratil@kde.org>
+
+ 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 "sessionthread_p.h"
+
+#include <QThread>
+
+Q_DECLARE_METATYPE(Akonadi::Connection::ConnectionType)
+Q_DECLARE_METATYPE(Akonadi::Connection *)
+
+using namespace Akonadi;
+
+SessionThread::SessionThread(QObject *parent)
+ : QObject(parent)
+{
+ qRegisterMetaType<Connection::ConnectionType>();
+ qRegisterMetaType<Connection*>();
+
+ QThread *thread = new QThread();
+ moveToThread(thread);
+ thread->start();
+}
+
+SessionThread::~SessionThread()
+{
+ QMetaObject::invokeMethod(this, "doThreadQuit");
+ if (!thread()->wait(10 * 1000)) {
+ thread()->terminate();
+ // Make sure to wait until it's done, otherwise it can crash when the pthread callback is called
+ thread()->wait();
+ }
+ delete thread();
+}
+
+Connection *SessionThread::createConnection(Connection::ConnectionType connectionType,
+ const QByteArray &sessionId)
+{
+ Connection *conn = Q_NULLPTR;
+ const bool invoke = QMetaObject::invokeMethod(this, "doCreateConnection",
+ Qt::BlockingQueuedConnection,
+ Q_RETURN_ARG(Akonadi::Connection*, conn),
+ Q_ARG(Akonadi::Connection::ConnectionType, connectionType),
+ Q_ARG(QByteArray, sessionId));
+ Q_ASSERT(invoke); Q_UNUSED(invoke);
+ return conn;
+}
+
+Connection *SessionThread::doCreateConnection(Connection::ConnectionType connType,
+ const QByteArray& sessionId)
+{
+ Q_ASSERT(thread() == QThread::currentThread());
+
+ Connection *conn = new Connection(connType, sessionId);
+ conn->moveToThread(thread());
+ connect(conn, &QObject::destroyed,
+ this, [this](QObject *obj) {
+ mConnections.removeOne(static_cast<Connection*>(obj));
+ });
+ return conn;
+}
+
+
+void SessionThread::doThreadQuit()
+{
+ Q_FOREACH (Connection *conn, mConnections) {
+ conn->closeConnection();
+ delete conn;
+ }
+
+ thread()->quit();
+}
diff --git a/src/core/sessionthread_p.h b/src/core/sessionthread_p.h
new file mode 100644
index 0000000..2e37c4b
--- /dev/null
+++ b/src/core/sessionthread_p.h
@@ -0,0 +1,54 @@
+/*
+ Copyright (c) 2016 Daniel Vrátil <dvratil@kde.org>
+
+ 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.
+*/
+
+#ifndef SESSIONTHREAD_P_H
+#define SESSIONTHREAD_P_H
+
+#include <QObject>
+#include <QVector>
+
+#include "connection_p.h"
+
+namespace Akonadi
+{
+
+class SessionThread : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit SessionThread(QObject *parent = Q_NULLPTR);
+ ~SessionThread();
+
+ Connection *createConnection(Connection::ConnectionType connType, const QByteArray &sessionId);
+
+private:
+ Q_INVOKABLE Akonadi::Connection *doCreateConnection(Akonadi::Connection::ConnectionType connType,
+ const QByteArray &sessionId);
+
+ Q_INVOKABLE void doThreadQuit();
+
+private:
+ QVector<Connection *> mConnections;
+};
+
+}
+
+
+#endif
diff --git a/src/core/subscriptionmonitor.cpp b/src/core/subscriptionmonitor.cpp
new file mode 100644
index 0000000..5b0bb84
--- /dev/null
+++ b/src/core/subscriptionmonitor.cpp
@@ -0,0 +1,247 @@
+/*
+ Copyright (c) 2016 Daniel Vrátil <dvratil@kde.org>
+
+ 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 "subscriptionmonitor.h"
+#include "monitor_p.h"
+
+using namespace Akonadi;
+
+namespace Akonadi
+{
+
+class SubscriberPrivate : public QSharedData
+{
+public:
+ SubscriberPrivate()
+ : QSharedData()
+ , allMonitored(false)
+ , isExclusive(false)
+ {}
+
+ SubscriberPrivate(const SubscriberPrivate &other)
+ : QSharedData(other)
+ , name(other.name)
+ , sessionId(other.sessionId)
+ , collections(other.collections)
+ , items(other.items)
+ , tags(other.tags)
+ , types(other.types)
+ , mimeTypes(other.mimeTypes)
+ , resources(other.resources)
+ , ignoredSessions(other.ignoredSessions)
+ , allMonitored(other.allMonitored)
+ , isExclusive(other.isExclusive)
+ {}
+
+ QByteArray name;
+ QByteArray sessionId;
+ QSet<Collection::Id> collections;
+ QSet<Item::Id> items;
+ QSet<Tag::Id> tags;
+ QSet<Subscriber::Type> types;
+ QSet<QString> mimeTypes;
+ QSet<QByteArray> resources;
+ QSet<QByteArray> ignoredSessions;
+ bool allMonitored;
+ bool isExclusive;
+};
+
+class SubscriptionMonitorPrivate : public MonitorPrivate
+{
+public:
+ SubscriptionMonitorPrivate(SubscriptionMonitor *qq)
+ : MonitorPrivate(Q_NULLPTR, qq)
+ {
+ pendingModification.startMonitoringType(Protocol::ModifySubscriptionCommand::SubscriptionChanges);
+ }
+
+ void slotNotify(const Protocol::ChangeNotification &msg) Q_DECL_OVERRIDE
+ {
+ if (msg.type() == Protocol::Command::SubscriptionChangeNotification) {
+ emitSubscriptionNotification(msg);
+ } else {
+ MonitorPrivate::slotNotify(msg);
+ }
+ }
+
+ void emitSubscriptionNotification(const Protocol::SubscriptionChangeNotification &msg)
+ {
+ Q_Q(SubscriptionMonitor);
+
+ Subscriber subscriber;
+ subscriber.d->name = msg.subscriber();
+ if (!msg.isRemove()) {
+ // NOTE: I have slightly over-designed the change notifications to
+ // have the "added" and "removed" API without realizing we don't really
+ // need that in the client API. The server does not use the "removed"
+ // API either but if we need that in the future, we can easily change
+ // it to use it (i.e. to send incremental changes rather than current
+ // full state of subscription) and adjust the signal APIs in this class
+ subscriber.d->sessionId = msg.sessionId();
+ subscriber.d->collections = msg.addedCollections();
+ subscriber.d->items = msg.addedItems();
+ subscriber.d->tags = msg.addedTags();
+ Q_FOREACH (Protocol::ModifySubscriptionCommand::ChangeType type, msg.addedTypes()) {
+ subscriber.d->types.insert(static_cast<Subscriber::Type>(type));
+ }
+ subscriber.d->mimeTypes = msg.addedMimeTypes();
+ subscriber.d->resources = msg.addedResources();
+ subscriber.d->ignoredSessions = msg.addedIgnoredSessions();
+ subscriber.d->allMonitored = msg.isAllMonitored();
+ subscriber.d->isExclusive = msg.isExclusive();
+ }
+
+ switch (msg.operation()) {
+ case Protocol::SubscriptionChangeNotification::Add:
+ Q_EMIT q->subscriptionAdded(subscriber);
+ break;
+ case Protocol::SubscriptionChangeNotification::Modify:
+ Q_EMIT q->subscriptionChanged(subscriber);
+ break;
+ case Protocol::SubscriptionChangeNotification::Remove:
+ Q_EMIT q->subscriptionRemoved(subscriber.name());
+ break;
+ default:
+ Q_ASSERT_X(false, __FUNCTION__, "Unknown operation type");
+ break;
+ }
+ }
+
+private:
+ Q_DECLARE_PUBLIC(SubscriptionMonitor)
+};
+
+}
+
+Subscriber::Subscriber()
+ : d(new SubscriberPrivate)
+{
+}
+
+Subscriber::Subscriber(const Subscriber &other)
+ : d(other.d)
+{
+}
+
+Subscriber::~Subscriber()
+{
+}
+
+Subscriber &Subscriber::operator=(const Subscriber &other)
+{
+ if (*this == other) {
+ return *this;
+ }
+ d = other.d;
+ return *this;
+}
+
+bool Subscriber::operator==(const Subscriber &other) const
+{
+ return d->name == other.d->name
+ && d->sessionId == other.d->sessionId
+ && d->collections == other.d->collections
+ && d->items == other.d->items
+ && d->tags == other.d->tags
+ && d->types == other.d->types
+ && d->mimeTypes == other.d->mimeTypes
+ && d->resources == other.d->resources
+ && d->ignoredSessions == other.d->ignoredSessions
+ && d->allMonitored == other.d->allMonitored
+ && d->isExclusive == other.d->isExclusive;
+}
+
+QByteArray Subscriber::name() const
+{
+ return d->name;
+}
+
+QByteArray Subscriber::sessionId() const
+{
+ return d->sessionId;
+}
+
+QSet<Collection::Id> Subscriber::monitoredCollections() const
+{
+ return d->collections;
+}
+
+QSet<Item::Id> Subscriber::monitoredItems() const
+{
+ return d->items;
+}
+
+QSet<Tag::Id> Subscriber::monitoredTags() const
+{
+ return d->tags;
+}
+
+QSet<Subscriber::Type> Subscriber::monitoredTypes() const
+{
+ return d->types;
+}
+
+QSet<QString> Subscriber::monitoredMimeTypes() const
+{
+ return d->mimeTypes;
+}
+
+QSet<QByteArray> Subscriber::monitoredResources() const
+{
+ return d->resources;
+}
+
+QSet<QByteArray> Subscriber::ignoredSessions() const
+{
+ return d->ignoredSessions;
+}
+
+bool Subscriber::monitorsAll() const
+{
+ return d->allMonitored;
+}
+
+bool Subscriber::isExclusive() const
+{
+ return d->isExclusive;
+}
+
+
+
+
+SubscriptionMonitor::SubscriptionMonitor(QObject *parent)
+ : Monitor(new SubscriptionMonitorPrivate(this), parent)
+{
+}
+
+SubscriptionMonitor::~SubscriptionMonitor()
+{
+}
+
+void SubscriptionMonitor::setSession(Akonadi::Session *session)
+{
+ Monitor::setSession(session);
+}
+
+Session *SubscriptionMonitor::session() const
+{
+ return Monitor::session();
+}
+
+
diff --git a/src/core/subscriptionmonitor.h b/src/core/subscriptionmonitor.h
new file mode 100644
index 0000000..525977f
--- /dev/null
+++ b/src/core/subscriptionmonitor.h
@@ -0,0 +1,123 @@
+/*
+ Copyright (c) 2016 Daniel Vrátil <dvratil@kde.org>
+
+ 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.
+*/
+
+#ifndef AKONADI_SUBSCRIPTIONMONITOR
+#define AKONADI_SUBSCRIPTIONMONITOR
+
+#include "monitor.h"
+#include "akonadicore_export.h"
+
+
+namespace Akonadi
+{
+
+class SubscriptionMonitorPrivate;
+class SubscriberPrivate;
+class AKONADICORE_EXPORT Subscriber
+{
+public:
+ enum Type {
+ InvalidType,
+ Collection,
+ Item,
+ Tag,
+ Relation,
+ Subscription
+ };
+
+ explicit Subscriber();
+ Subscriber(const Subscriber &other);
+ ~Subscriber();
+
+ Subscriber &operator=(const Subscriber &other);
+ bool operator==(const Subscriber &other) const;
+
+ QByteArray name() const;
+ QByteArray sessionId() const;
+
+ QSet<Collection::Id> monitoredCollections() const;
+ QSet<Item::Id> monitoredItems() const;
+ QSet<Tag::Id> monitoredTags() const;
+ QSet<Type> monitoredTypes() const;
+ QSet<QString> monitoredMimeTypes() const;
+ QSet<QByteArray> monitoredResources() const;
+ QSet<QByteArray> ignoredSessions() const;
+ bool monitorsAll() const;
+ bool isExclusive() const;
+
+private:
+ QSharedDataPointer<SubscriberPrivate> d;
+
+ friend class SubscriptionMonitorPrivate;
+};
+
+class SubscriptionMonitorPrivate;
+/**
+ * @short Monitors change notification subscription changes
+ *
+ * The SubscriptionMonitor is a specialized Monitor which monitors only for
+ * changes in notification subscriptions.
+ *
+ * This class is only useful for developer tools like Akonadi Console which
+ * want to show and introspect the notification system and should not ever be
+ * user in normal applications.
+ *
+ * @since 16.04
+ */
+class AKONADICORE_EXPORT SubscriptionMonitor : protected Monitor
+{
+ Q_OBJECT
+
+public:
+ explicit SubscriptionMonitor(QObject *parent = Q_NULLPTR);
+
+ ~SubscriptionMonitor();
+
+ void setSession(Session *session);
+
+ Session *session() const;
+
+Q_SIGNALS:
+ /**
+ * This signal is emitted when a new Monitor subscribes to notifications.
+ * Once this monitor is set up and registered to Akonadi it will also be
+ * emitted once for each existing subscriber so that applications can
+ * initially populate their list of subscribers.
+ */
+ void subscriptionAdded(const Subscriber &subscription);
+
+ /**
+ * This signal is emitted when an existing subscriber changes its subscription
+ * settings.
+ */
+ void subscriptionChanged(const Subscriber &subscription);
+
+ /**
+ * This signal is emitted when an existing subscriber unsubscribes from the
+ * server, i.e. when a Monitor is destroyed.
+ */
+ void subscriptionRemoved(const QByteArray &subscriber);
+
+private:
+ friend class SubscriptionMonitorPrivate;
+};
+
+}
+
+#endif
diff --git a/src/private/protocol.cpp b/src/private/protocol.cpp
index 83765b6..0ef7f2e 100644
--- a/src/private/protocol.cpp
+++ b/src/private/protocol.cpp
@@ -132,8 +132,20 @@ QDebug operator<<(QDebug _dbg, Akonadi::Protocol::Command::Type type)
case Akonadi::Protocol::Command::StreamPayload:
return dbg << "StreamPayload";
- case Akonadi::Protocol::Command::ChangeNotification:
- return dbg << "ChangeNotification";
+ case Akonadi::Protocol::Command::ItemChangeNotification:
+ return dbg << "ItemChangeNotification";
+ case Akonadi::Protocol::Command::CollectionChangeNotification:
+ return dbg << "CollectionChangeNotification";
+ case Akonadi::Protocol::Command::TagChangeNotification:
+ return dbg << "TagChangeNotification";
+ case Akonadi::Protocol::Command::RelationChangeNotification:
+ return dbg << "RelationChangeNotification";
+ case Akonadi::Protocol::Command::SubscriptionChangeNotification:
+ return dbg << "SubscriptionChangeNotification";
+ case Akonadi::Protocol::Command::CreateSubscription:
+ return dbg << "CreateSubscription";
+ case Akonadi::Protocol::Command::ModifySubscription:
+ return dbg << "ModifySubscription";
case Akonadi::Protocol::Command::_ResponseBit:
Q_ASSERT(false);
@@ -144,6 +156,12 @@ QDebug operator<<(QDebug _dbg, Akonadi::Protocol::Command::Type type)
return dbg;
}
+QDebug operator<<(QDebug _dbg, const Akonadi::Protocol::ItemChangeNotification::Relation &rel)
+{
+ QDebug dbg(_dbg.noquote());
+ return dbg << "Left: " << rel.leftId << ", Right:" << rel.rightId << ", Type: " << rel.type;
+}
+
namespace Akonadi
{
namespace Protocol
@@ -547,7 +565,12 @@ public:
// Other...?
registerType<Command::StreamPayload, StreamPayloadCommand, StreamPayloadResponse>();
- registerType<Command::ChangeNotification, ChangeNotification, Response /* invalid */>();
+ registerType<Command::ItemChangeNotification, ItemChangeNotification, Response /* invalid */>();
+ registerType<Command::CollectionChangeNotification, CollectionChangeNotification, Response /* invalid */>();
+ registerType<Command::TagChangeNotification, TagChangeNotification, Response /* invalid */>();
+ registerType<Command::RelationChangeNotification, RelationChangeNotification, Response /* invalid */>();
+ registerType<Command::CreateSubscription, CreateSubscriptionCommand, CreateSubscriptionResponse>();
+ registerType<Command::ModifySubscription, ModifySubscriptionCommand, ModifySubscriptionResponse>();
}
// clang has problem resolving the right qHash() overload for Command::Type,
@@ -1642,55 +1665,39 @@ DataStream &operator>>(DataStream &stream, HelloResponse &command)
class LoginCommandPrivate : public CommandPrivate
{
public:
- LoginCommandPrivate(const QByteArray &sessionId = QByteArray(),
- LoginCommand::SessionMode mode = LoginCommand::CommandMode)
+ LoginCommandPrivate(const QByteArray &sessionId = QByteArray())
: CommandPrivate(Command::Login)
, sessionId(sessionId)
- , sessionMode(mode)
{}
LoginCommandPrivate(const LoginCommandPrivate &other)
: CommandPrivate(other)
, sessionId(other.sessionId)
- , sessionMode(other.sessionMode)
{}
bool compare(const CommandPrivate *other) const Q_DECL_OVERRIDE
{
return CommandPrivate::compare(other)
- && COMPARE(sessionId)
- && COMPARE(sessionMode);
+ && COMPARE(sessionId);
}
DataStream &serialize(DataStream &stream) const Q_DECL_OVERRIDE
{
return CommandPrivate::serialize(stream)
- << sessionId
- << sessionMode;
+ << sessionId;
}
DataStream &deserialize(DataStream &stream) Q_DECL_OVERRIDE
{
return CommandPrivate::deserialize(stream)
- >> sessionId
- >> sessionMode;
+ >> sessionId;
}
void debugString(DebugBlock &blck) const Q_DECL_OVERRIDE
{
CommandPrivate::debugString(blck);
blck.write("Session ID", sessionId);
- blck.write("Session mode", [this]() -> QString {
- switch (sessionMode) {
- case LoginCommand::CommandMode:
- return QStringLiteral("CommandMode");
- case LoginCommand::NotificationBus:
- return QStringLiteral("NotificationBus");
- }
- Q_ASSERT(false);
- return QString();
- }());
}
CommandPrivate *clone() const Q_DECL_OVERRIDE
@@ -1699,7 +1706,6 @@ public:
}
QByteArray sessionId;
- LoginCommand::SessionMode sessionMode;
};
@@ -1712,8 +1718,8 @@ LoginCommand::LoginCommand()
{
}
-LoginCommand::LoginCommand(const QByteArray &sessionId, SessionMode mode)
- : Command(new LoginCommandPrivate(sessionId, mode))
+LoginCommand::LoginCommand(const QByteArray &sessionId)
+ : Command(new LoginCommandPrivate(sessionId))
{
}
@@ -1733,16 +1739,6 @@ QByteArray LoginCommand::sessionId() const
return d_func()->sessionId;
}
-void LoginCommand::setSessionMode(SessionMode mode)
-{
- d_func()->sessionMode = mode;
-}
-
-LoginCommand::SessionMode LoginCommand::sessionMode() const
-{
- return d_func()->sessionMode;
-}
-
DataStream &operator<<(DataStream &stream, const LoginCommand &command)
{
return command.d_func()->serialize(stream);
@@ -8166,22 +8162,147 @@ DataStream &operator>>(DataStream &stream, StreamPayloadResponse &command)
/******************************************************************************/
+class ChangeNotificationPrivate : public CommandPrivate
+{
+public:
+ ChangeNotificationPrivate(Command::Type type)
+ : CommandPrivate(type)
+ {
+ }
+
+ ChangeNotificationPrivate(const ChangeNotificationPrivate &other)
+ : CommandPrivate(other)
+ , sessionId(other.sessionId)
+ , metadata(other.metadata)
+ {
+ }
+ virtual bool compare(const Akonadi::Protocol::CommandPrivate *other) const Q_DECL_OVERRIDE
+ {
+ return CommandPrivate::compare(other)
+ && COMPARE(sessionId);
+ }
-class ChangeNotificationPrivate : public CommandPrivate
+ virtual DataStream &serialize(DataStream &stream) const Q_DECL_OVERRIDE
+ {
+ return CommandPrivate::serialize(stream)
+ << sessionId;
+ }
+
+ virtual DataStream &deserialize(DataStream &stream) Q_DECL_OVERRIDE
+ {
+ return CommandPrivate::deserialize(stream)
+ >> sessionId;
+ }
+
+ QByteArray sessionId;
+
+
+ // For internal use only: Akonadi server can add some additional information
+ // that might be useful when evaluating the notification for example, but
+ // it is never transferred to clients
+ QVector<QByteArray> metadata;
+};
+
+AKONADI_DECLARE_PRIVATE(ChangeNotification)
+
+ChangeNotification::ChangeNotification()
+ : Command()
+{
+}
+
+ChangeNotification::ChangeNotification(ChangeNotificationPrivate *dptr)
+ : Command(dptr)
+{
+}
+
+ChangeNotification::ChangeNotification(const Command &other)
+ : Command(other)
+{
+}
+
+bool ChangeNotification::isRemove() const
+{
+ switch (type()) {
+ case Command::Invalid:
+ return false;
+ case Command::ItemChangeNotification:
+ return static_cast<const Protocol::ItemChangeNotification*>(this)->operation() == ItemChangeNotification::Remove;
+ case Command::CollectionChangeNotification:
+ return static_cast<const Protocol::CollectionChangeNotification*>(this)->operation() == CollectionChangeNotification::Remove;
+ case Command::TagChangeNotification:
+ return static_cast<const Protocol::TagChangeNotification*>(this)->operation() == TagChangeNotification::Remove;
+ case Command::RelationChangeNotification:
+ return static_cast<const Protocol::RelationChangeNotification*>(this)->operation() == RelationChangeNotification::Remove;
+ case Command::SubscriptionChangeNotification:
+ return static_cast<const Protocol::SubscriptionChangeNotification*>(this)->operation() == SubscriptionChangeNotification::Remove;
+ default:
+ Q_ASSERT_X(false, __FUNCTION__, "Unknown ChangeNotification type");
+ }
+
+ return false;
+}
+
+bool ChangeNotification::isMove() const
+{
+ switch (type()) {
+ case Command::Invalid:
+ return false;
+ case Command::ItemChangeNotification:
+ return static_cast<const Protocol::ItemChangeNotification*>(this)->operation() == ItemChangeNotification::Move;
+ case Command::CollectionChangeNotification:
+ return static_cast<const Protocol::CollectionChangeNotification*>(this)->operation() == CollectionChangeNotification::Move;
+ case Command::TagChangeNotification:
+ case Command::RelationChangeNotification:
+ case Command::SubscriptionChangeNotification:
+ return false;
+ default:
+ Q_ASSERT_X(false, __FUNCTION__, "Unknown ChangeNotification type");
+ }
+
+ return false;
+}
+
+void ChangeNotification::setSessionId(const QByteArray &sessionId)
+{
+ d_func()->sessionId = sessionId;
+}
+
+QByteArray ChangeNotification::sessionId() const
+{
+ return d_func()->sessionId;
+}
+
+void ChangeNotification::addMetadata(const QByteArray &metadata)
+{
+ d_func()->metadata.append(metadata);
+}
+
+void ChangeNotification::removeMetadata(const QByteArray &metadata)
+{
+ d_func()->metadata.removeAll(metadata);
+}
+
+QVector<QByteArray> ChangeNotification::metadata() const
+{
+ return d_func()->metadata;
+}
+
+
+
+
+class ItemChangeNotificationPrivate : public ChangeNotificationPrivate
{
public:
- ChangeNotificationPrivate()
- : CommandPrivate(Command::ChangeNotification)
+ ItemChangeNotificationPrivate()
+ : ChangeNotificationPrivate(Command::ItemChangeNotification)
, parentCollection(-1)
, parentDestCollection(-1)
- , type(ChangeNotification::InvalidType)
- , operation(ChangeNotification::InvalidOp)
+ , operation(ItemChangeNotification::InvalidOp)
{}
- ChangeNotificationPrivate(const ChangeNotificationPrivate &other)
- : CommandPrivate(other)
- , sessionId(other.sessionId)
+ ItemChangeNotificationPrivate(const ItemChangeNotificationPrivate &other)
+ : ChangeNotificationPrivate(other)
, items(other.items)
, resource(other.resource)
, destResource(other.destResource)
@@ -8190,47 +8311,40 @@ public:
, removedFlags(other.removedFlags)
, addedTags(other.addedTags)
, removedTags(other.removedTags)
- , metadata(other.metadata)
+ , addedRelations(other.addedRelations)
+ , removedRelations(other.removedRelations)
, parentCollection(other.parentCollection)
, parentDestCollection(other.parentDestCollection)
- , type(other.type)
, operation(other.operation)
{}
- bool compareWithoutOpAndParts(const ChangeNotificationPrivate *other) const
- {
- return type == other->type
- && items == other->items
- && sessionId == other->sessionId
- && resource == other->resource
- && destResource == other->destResource
- && parentCollection == other->parentCollection
- && parentDestCollection == other->parentDestCollection;
- }
-
bool compare(const CommandPrivate *other) const Q_DECL_OVERRIDE
{
- return CommandPrivate::compare(other)
+ return ChangeNotificationPrivate::compare(other)
&& COMPARE(operation)
&& COMPARE(parts)
&& COMPARE(addedFlags)
&& COMPARE(removedFlags)
&& COMPARE(addedTags)
&& COMPARE(removedTags)
- && compareWithoutOpAndParts(static_cast<const ChangeNotificationPrivate*>(other));
+ && COMPARE(addedRelations)
+ && COMPARE(removedRelations)
+ && COMPARE(items)
+ && COMPARE(resource)
+ && COMPARE(destResource)
+ && COMPARE(parentCollection)
+ && COMPARE(parentDestCollection);
}
CommandPrivate *clone() const Q_DECL_OVERRIDE
{
- return new ChangeNotificationPrivate(*this);
+ return new ItemChangeNotificationPrivate(*this);
}
DataStream &serialize(DataStream &stream) const Q_DECL_OVERRIDE
{
- return CommandPrivate::serialize(stream)
- << type
+ return ChangeNotificationPrivate::serialize(stream)
<< operation
- << sessionId
<< items
<< resource
<< destResource
@@ -8239,16 +8353,16 @@ public:
<< removedFlags
<< addedTags
<< removedTags
+ << addedRelations
+ << removedRelations
<< parentCollection
<< parentDestCollection;
}
DataStream &deserialize(DataStream &stream) Q_DECL_OVERRIDE
{
- return CommandPrivate::deserialize(stream)
- >> type
+ return ChangeNotificationPrivate::deserialize(stream)
>> operation
- >> sessionId
>> items
>> resource
>> destResource
@@ -8257,60 +8371,42 @@ public:
>> removedFlags
>> addedTags
>> removedTags
+ >> addedRelations
+ >> removedRelations
>> parentCollection
>> parentDestCollection;
}
void debugString(DebugBlock &blck) const Q_DECL_OVERRIDE
{
- blck.write("Type", [this]() -> QString {
- switch (type) {
- case ChangeNotification::Items:
- return QStringLiteral("Items");
- case ChangeNotification::Collections:
- return QStringLiteral("Collections");
- case ChangeNotification::Tags:
- return QStringLiteral("Tags");
- case ChangeNotification::Relations:
- return QStringLiteral("Relations");
- case ChangeNotification::InvalidType:
- return QStringLiteral("*INVALID TYPE*");
- }
- Q_ASSERT(false);
- return QString();
- }());
blck.write("Operation", [this]() -> QString {
switch (operation) {
- case ChangeNotification::Add:
+ case ItemChangeNotification::Add:
return QStringLiteral("Add");
- case ChangeNotification::Modify:
+ case ItemChangeNotification::Modify:
return QStringLiteral("Modify");
- case ChangeNotification::ModifyFlags:
+ case ItemChangeNotification::ModifyFlags:
return QStringLiteral("ModifyFlags");
- case ChangeNotification::ModifyTags:
+ case ItemChangeNotification::ModifyTags:
return QStringLiteral("ModifyTags");
- case ChangeNotification::ModifyRelations:
+ case ItemChangeNotification::ModifyRelations:
return QStringLiteral("ModifyRelations");
- case ChangeNotification::Move:
+ case ItemChangeNotification::Move:
return QStringLiteral("Move");
- case ChangeNotification::Remove:
+ case ItemChangeNotification::Remove:
return QStringLiteral("Remove");
- case ChangeNotification::Link:
+ case ItemChangeNotification::Link:
return QStringLiteral("Link");
- case ChangeNotification::Unlink:
+ case ItemChangeNotification::Unlink:
return QStringLiteral("Unlink");
- case ChangeNotification::Subscribe:
- return QStringLiteral("Subscribe");
- case ChangeNotification::Unsubscribe:
- return QStringLiteral("Unsubscribe");
- case ChangeNotification::InvalidOp:
+ case ItemChangeNotification::InvalidOp:
return QStringLiteral("*INVALID OPERATION*");
}
Q_ASSERT(false);
return QString();
}());
blck.beginBlock("Items");
- Q_FOREACH (const ChangeNotification::Entity &item, items) {
+ Q_FOREACH (const ItemChangeNotification::Item &item, items) {
blck.beginBlock();
blck.write("ID", item.id);
blck.write("RemoteID", item.remoteId);
@@ -8319,7 +8415,6 @@ public:
blck.endBlock();
}
blck.endBlock();
- blck.write("Session", sessionId);
blck.write("Resource", resource);
blck.write("Destination Resource", destResource);
blck.write("Parent Collection", parentCollection);
@@ -8329,12 +8424,11 @@ public:
blck.write("Removed Flags", removedFlags);
blck.write("Added Tags", addedTags);
blck.write("Removed Tags", removedTags);
+ blck.write("Added Relations", addedRelations);
+ blck.write("Removed Relations", removedRelations);
}
-
-
- QByteArray sessionId;
- QMap<qint64, ChangeNotification::Entity> items;
+ QMap<qint64, ItemChangeNotification::Item> items;
QByteArray resource;
QByteArray destResource;
QSet<QByteArray> parts;
@@ -8342,231 +8436,495 @@ public:
QSet<QByteArray> removedFlags;
QSet<qint64> addedTags;
QSet<qint64> removedTags;
-
- // For internal use only: Akonadi server can add some additional information
- // that might be useful when evaluating the notification for example, but
- // it is never transferred to clients
- QVector<QByteArray> metadata;
+ QSet<ItemChangeNotification::Relation> addedRelations;
+ QSet<ItemChangeNotification::Relation> removedRelations;
qint64 parentCollection;
qint64 parentDestCollection;
- ChangeNotification::Type type;
- ChangeNotification::Operation operation;
+ ItemChangeNotification::Operation operation;
};
-AKONADI_DECLARE_PRIVATE(ChangeNotification)
+AKONADI_DECLARE_PRIVATE(ItemChangeNotification)
-ChangeNotification::ChangeNotification()
- : Command(new ChangeNotificationPrivate)
+ItemChangeNotification::ItemChangeNotification()
+ : ChangeNotification(new ItemChangeNotificationPrivate)
{
}
-ChangeNotification::ChangeNotification(const Command &other)
- : Command(other)
+ItemChangeNotification::ItemChangeNotification(const Command &other)
+ : ChangeNotification(other)
{
- checkCopyInvariant(Command::ChangeNotification);
+ checkCopyInvariant(Command::ItemChangeNotification);
}
-bool ChangeNotification::isValid() const
+bool ItemChangeNotification::isValid() const
{
- Q_D(const ChangeNotification);
- return d->commandType == Command::ChangeNotification
- && d->type != InvalidType
+ Q_D(const ItemChangeNotification);
+ return d->commandType == Command::ItemChangeNotification
&& d->operation != InvalidOp;
}
-void ChangeNotification::addEntity(Id id, const QString &remoteId, const QString &remoteRevision, const QString &mimeType)
+void ItemChangeNotification::addItem(Id id, const QString &remoteId, const QString &remoteRevision, const QString &mimeType)
{
- d_func()->items.insert(id, Entity(id, remoteId, remoteRevision, mimeType));
+ d_func()->items.insert(id, Item(id, remoteId, remoteRevision, mimeType));
}
-void ChangeNotification::setEntities(const QVector<Entity> &entities)
+void ItemChangeNotification::setItem(const QVector<Item> &items)
{
- Q_D(ChangeNotification);
- clearEntities();
- Q_FOREACH (const Entity &entity, entities) {
- d->items.insert(entity.id, entity);
+ Q_D(ItemChangeNotification);
+ clearItems();
+ Q_FOREACH (const Item &item, items) {
+ d->items.insert(item.id, item);
}
}
-void ChangeNotification::clearEntities()
+void ItemChangeNotification::clearItems()
{
d_func()->items.clear();
}
-QMap<qint64, ChangeNotification::Entity> ChangeNotification::entities() const
+QMap<qint64, ItemChangeNotification::Item> ItemChangeNotification::items() const
{
return d_func()->items;
}
-ChangeNotification::Entity ChangeNotification::entity(const Id id) const
+ItemChangeNotification::Item ItemChangeNotification::item(const Id id) const
{
return d_func()->items.value(id);
}
-QList<qint64> ChangeNotification::uids() const
+QList<qint64> ItemChangeNotification::uids() const
{
return d_func()->items.keys();
}
-QByteArray ChangeNotification::sessionId() const
-{
- return d_func()->sessionId;
-}
-
-void ChangeNotification::setSessionId(const QByteArray &sessionId)
-{
- d_func()->sessionId = sessionId;
-}
-
-ChangeNotification::Type ChangeNotification::type() const
-{
- return d_func()->type;
-}
-
-void ChangeNotification::setType(Type type)
-{
- d_func()->type = type;
-}
-
-ChangeNotification::Operation ChangeNotification::operation() const
+ItemChangeNotification::Operation ItemChangeNotification::operation() const
{
return d_func()->operation;
}
-void ChangeNotification::setOperation(Operation operation)
+void ItemChangeNotification::setOperation(Operation operation)
{
d_func()->operation = operation;
}
-QByteArray ChangeNotification::resource() const
+QByteArray ItemChangeNotification::resource() const
{
return d_func()->resource;
}
-void ChangeNotification::setResource(const QByteArray &resource)
+void ItemChangeNotification::setResource(const QByteArray &resource)
{
d_func()->resource = resource;
}
-qint64 ChangeNotification::parentCollection() const
+qint64 ItemChangeNotification::parentCollection() const
{
return d_func()->parentCollection;
}
-qint64 ChangeNotification::parentDestCollection() const
+qint64 ItemChangeNotification::parentDestCollection() const
{
return d_func()->parentDestCollection;
}
-void ChangeNotification::setParentCollection(Id parent)
+void ItemChangeNotification::setParentCollection(Id parent)
{
d_func()->parentCollection = parent;
}
-void ChangeNotification::setParentDestCollection(Id parent)
+void ItemChangeNotification::setParentDestCollection(Id parent)
{
d_func()->parentDestCollection = parent;
}
-void ChangeNotification::setDestinationResource(const QByteArray &destResource)
+void ItemChangeNotification::setDestinationResource(const QByteArray &destResource)
{
d_func()->destResource = destResource;
}
-QByteArray ChangeNotification::destinationResource() const
+QByteArray ItemChangeNotification::destinationResource() const
{
return d_func()->destResource;
}
-QSet<QByteArray> ChangeNotification::itemParts() const
+QSet<QByteArray> ItemChangeNotification::itemParts() const
{
return d_func()->parts;
}
-void ChangeNotification::setItemParts(const QSet<QByteArray> &parts)
+void ItemChangeNotification::setItemParts(const QSet<QByteArray> &parts)
{
d_func()->parts = parts;
}
-QSet<QByteArray> ChangeNotification::addedFlags() const
+QSet<QByteArray> ItemChangeNotification::addedFlags() const
{
return d_func()->addedFlags;
}
-void ChangeNotification::setAddedFlags(const QSet<QByteArray> &addedFlags)
+void ItemChangeNotification::setAddedFlags(const QSet<QByteArray> &addedFlags)
{
d_func()->addedFlags = addedFlags;
}
-QSet<QByteArray> ChangeNotification::removedFlags() const
+QSet<QByteArray> ItemChangeNotification::removedFlags() const
{
return d_func()->removedFlags;
}
-void ChangeNotification::setRemovedFlags(const QSet<QByteArray> &removedFlags)
+void ItemChangeNotification::setRemovedFlags(const QSet<QByteArray> &removedFlags)
{
d_func()->removedFlags = removedFlags;
}
-QSet<qint64> ChangeNotification::addedTags() const
+QSet<qint64> ItemChangeNotification::addedTags() const
{
return d_func()->addedTags;
}
-void ChangeNotification::setAddedTags(const QSet<qint64> &addedTags)
+void ItemChangeNotification::setAddedTags(const QSet<qint64> &addedTags)
{
d_func()->addedTags = addedTags;
}
-QSet<qint64> ChangeNotification::removedTags() const
+QSet<qint64> ItemChangeNotification::removedTags() const
{
return d_func()->removedTags;
}
-void ChangeNotification::setRemovedTags(const QSet<qint64> &removedTags)
+void ItemChangeNotification::setRemovedTags(const QSet<qint64> &removedTags)
{
d_func()->removedTags = removedTags;
}
-void ChangeNotification::addMetadata(const QByteArray &metadata)
+QSet<ItemChangeNotification::Relation> ItemChangeNotification::addedRelations() const
{
- d_func()->metadata.append(metadata);
+ return d_func()->addedRelations;
}
-void ChangeNotification::removeMetadata(const QByteArray &metadata)
+void ItemChangeNotification::setAddedRelations(const QSet<Relation> &relations)
{
- d_func()->metadata.removeAll(metadata);
+ d_func()->addedRelations = relations;
}
-QVector<QByteArray> ChangeNotification::metadata() const
+QSet<ItemChangeNotification::Relation> ItemChangeNotification::removedRelations() const
{
- return d_func()->metadata;
+ return d_func()->removedRelations;
+}
+
+void ItemChangeNotification::setRemovedRelations(const QSet<Relation> &relations)
+{
+ d_func()->removedRelations = relations;
+}
+
+DataStream &operator<<(DataStream &stream, const Akonadi::Protocol::ItemChangeNotification::Item &item)
+{
+ return stream << item.id
+ << item.remoteId
+ << item.remoteRevision
+ << item.mimeType;
}
-bool ChangeNotification::appendAndCompress(ChangeNotification::List &list, const ChangeNotification &msg)
+DataStream &operator>>(DataStream &stream, Akonadi::Protocol::ItemChangeNotification::Item &item)
+{
+ return stream >> item.id
+ >> item.remoteId
+ >> item.remoteRevision
+ >> item.mimeType;
+}
+
+DataStream &operator<<(DataStream &stream, const Akonadi::Protocol::ItemChangeNotification::Relation &relation)
+{
+ return stream << relation.leftId
+ << relation.rightId
+ << relation.type;
+}
+
+DataStream &operator>>(DataStream &stream, Akonadi::Protocol::ItemChangeNotification::Relation &relation)
+{
+ return stream >> relation.leftId
+ >> relation.rightId
+ >> relation.type;
+}
+
+DataStream &operator<<(DataStream &stream, const Akonadi::Protocol::ItemChangeNotification &command)
+{
+ return command.d_func()->serialize(stream);
+}
+
+DataStream &operator>>(DataStream &stream, Akonadi::Protocol::ItemChangeNotification &command)
+{
+ return command.d_func()->deserialize(stream);
+}
+
+
+
+
+
+class CollectionChangeNotificationPrivate : public ChangeNotificationPrivate
+{
+public:
+ CollectionChangeNotificationPrivate()
+ : ChangeNotificationPrivate(Command::CollectionChangeNotification)
+ , id(-1)
+ , parentCollection(-1)
+ , parentDestCollection(-1)
+ , operation(CollectionChangeNotification::InvalidOp)
+ {}
+
+ CollectionChangeNotificationPrivate(const CollectionChangeNotificationPrivate &other)
+ : ChangeNotificationPrivate(other)
+ , resource(other.resource)
+ , destResource(other.destResource)
+ , changedParts(other.changedParts)
+ , remoteId(other.remoteId)
+ , remoteRevision(other.remoteRevision)
+ , id(other.id)
+ , parentCollection(other.parentCollection)
+ , parentDestCollection(other.parentDestCollection)
+ , operation(other.operation)
+ {}
+
+ bool compareWithoutOpAndParts(const CollectionChangeNotificationPrivate *other) const
+ {
+ return id == other->id
+ && remoteId == other->remoteId
+ && remoteRevision == other->remoteRevision
+ && resource == other->resource
+ && destResource == other->destResource
+ && parentCollection == other->parentCollection
+ && parentDestCollection == other->parentDestCollection;
+ }
+
+ bool compare(const CommandPrivate *other) const Q_DECL_OVERRIDE
+ {
+ return ChangeNotificationPrivate::compare(other)
+ && COMPARE(operation)
+ && COMPARE(changedParts)
+ && compareWithoutOpAndParts(static_cast<const CollectionChangeNotificationPrivate*>(other));
+ }
+
+ CommandPrivate *clone() const Q_DECL_OVERRIDE
+ {
+ return new CollectionChangeNotificationPrivate(*this);
+ }
+
+ DataStream &serialize(DataStream &stream) const Q_DECL_OVERRIDE
+ {
+ return ChangeNotificationPrivate::serialize(stream)
+ << operation
+ << id
+ << remoteId
+ << remoteRevision
+ << resource
+ << destResource
+ << changedParts
+ << parentCollection
+ << parentDestCollection;
+ }
+
+ DataStream &deserialize(DataStream &stream) Q_DECL_OVERRIDE
+ {
+ return ChangeNotificationPrivate::deserialize(stream)
+ >> operation
+ >> id
+ >> remoteId
+ >> remoteRevision
+ >> resource
+ >> destResource
+ >> changedParts
+ >> parentCollection
+ >> parentDestCollection;
+ }
+
+ void debugString(DebugBlock &blck) const Q_DECL_OVERRIDE
+ {
+ blck.write("Operation", [this]() -> QString {
+ switch (operation) {
+ case CollectionChangeNotification::Add:
+ return QStringLiteral("Add");
+ case CollectionChangeNotification::Modify:
+ return QStringLiteral("Modify");
+ case CollectionChangeNotification::Move:
+ return QStringLiteral("Move");
+ case CollectionChangeNotification::Remove:
+ return QStringLiteral("Remove");
+ case CollectionChangeNotification::Subscribe:
+ return QStringLiteral("Subscribe");
+ case CollectionChangeNotification::Unsubscribe:
+ return QStringLiteral("Unsubscribe");
+ case ItemChangeNotification::InvalidOp:
+ return QStringLiteral("*INVALID OPERATION*");
+ }
+ Q_ASSERT(false);
+ return QString();
+ }());
+ blck.write("Col ID", id);
+ blck.write("Col RID", remoteId);
+ blck.write("Col RREV", remoteRevision);
+ blck.write("Resource", resource);
+ blck.write("Destination Resource", destResource);
+ blck.write("Parent Collection", parentCollection);
+ blck.write("Parent Destination Collection", parentDestCollection);
+ blck.write("Changed Parts", changedParts);
+ }
+
+ QByteArray resource;
+ QByteArray destResource;
+ QSet<QByteArray> changedParts;
+ QString remoteId;
+ QString remoteRevision;
+ qint64 id;
+ qint64 parentCollection;
+ qint64 parentDestCollection;
+ CollectionChangeNotification::Operation operation;
+};
+
+AKONADI_DECLARE_PRIVATE(CollectionChangeNotification)
+
+
+CollectionChangeNotification::CollectionChangeNotification()
+ : ChangeNotification(new CollectionChangeNotificationPrivate)
+{
+}
+
+CollectionChangeNotification::CollectionChangeNotification(const Command &other)
+ : ChangeNotification(other)
+{
+ checkCopyInvariant(Command::CollectionChangeNotification);
+}
+
+bool CollectionChangeNotification::isValid() const
+{
+ Q_D(const CollectionChangeNotification);
+ return d->commandType == Command::CollectionChangeNotification
+ && d->operation != InvalidOp;
+}
+
+CollectionChangeNotification::Id CollectionChangeNotification::id() const
+{
+ return d_func()->id;
+}
+
+void CollectionChangeNotification::setId(Id id)
+{
+ d_func()->id = id;
+}
+
+QString CollectionChangeNotification::remoteId() const
+{
+ return d_func()->remoteId;
+}
+
+void CollectionChangeNotification::setRemoteId(const QString &remoteId)
+{
+ d_func()->remoteId = remoteId;
+}
+
+QString CollectionChangeNotification::remoteRevision() const
+{
+ return d_func()->remoteRevision;
+}
+
+void CollectionChangeNotification::setRemoteRevision(const QString &remoteRevision)
+{
+ d_func()->remoteRevision = remoteRevision;
+}
+
+CollectionChangeNotification::Operation CollectionChangeNotification::operation() const
+{
+ return d_func()->operation;
+}
+
+void CollectionChangeNotification::setOperation(Operation operation)
+{
+ d_func()->operation = operation;
+}
+
+QByteArray CollectionChangeNotification::resource() const
+{
+ return d_func()->resource;
+}
+
+void CollectionChangeNotification::setResource(const QByteArray &resource)
+{
+ d_func()->resource = resource;
+}
+
+qint64 CollectionChangeNotification::parentCollection() const
+{
+ return d_func()->parentCollection;
+}
+
+qint64 CollectionChangeNotification::parentDestCollection() const
+{
+ return d_func()->parentDestCollection;
+}
+
+void CollectionChangeNotification::setParentCollection(Id parent)
+{
+ d_func()->parentCollection = parent;
+}
+
+void CollectionChangeNotification::setParentDestCollection(Id parent)
+{
+ d_func()->parentDestCollection = parent;
+}
+
+void CollectionChangeNotification::setDestinationResource(const QByteArray &destResource)
+{
+ d_func()->destResource = destResource;
+}
+
+QByteArray CollectionChangeNotification::destinationResource() const
+{
+ return d_func()->destResource;
+}
+
+QSet<QByteArray> CollectionChangeNotification::changedParts() const
+{
+ return d_func()->changedParts;
+}
+
+void CollectionChangeNotification::setChangedParts(const QSet<QByteArray> &changedParts)
+{
+ d_func()->changedParts = changedParts;
+}
+
+DataStream &operator<<(DataStream &stream, const Akonadi::Protocol::CollectionChangeNotification &command)
+{
+ return command.d_func()->serialize(stream);
+}
+
+DataStream &operator>>(DataStream &stream, Akonadi::Protocol::CollectionChangeNotification &command)
+{
+ return command.d_func()->deserialize(stream);
+}
+
+bool CollectionChangeNotification::appendAndCompress(ChangeNotification::List &list, const CollectionChangeNotification &msg)
{
//It is likely that compressable notifications are within the last few notifications, so avoid searching a list that is potentially huge
static const int maxCompressionSearchLength = 10;
int searchCounter = 0;
// There are often multiple Collection Modify notifications in the queue,
// so we optimize for this case.
- if (msg.type() == ChangeNotification::Collections && msg.operation() == ChangeNotification::Modify) {
- typename List::Iterator iter, begin;
+ if (msg.operation() == Modify) {
// We are iterating from end, since there's higher probability of finding
// matching notification
- for (iter = list.end(), begin = list.begin(); iter != begin; ) {
+ for (auto iter = list.end(), begin = list.begin(); iter != begin; ) {
--iter;
- if (msg.d_func()->compareWithoutOpAndParts((*iter).d_func())) {
+ CollectionChangeNotification &it = static_cast<CollectionChangeNotification&>(*iter);
+ if (msg.d_func()->compareWithoutOpAndParts(it.d_func())) {
// both are modifications, merge them together and drop the new one
- if (msg.operation() == ChangeNotification::Modify && iter->operation() == ChangeNotification::Modify) {
- iter->setItemParts(iter->itemParts() + msg.itemParts());
+ if (msg.operation() == Modify && it.operation() == Modify) {
+ it.setChangedParts(it.changedParts() + msg.changedParts());
return false;
}
// we found Add notification, which means we can drop this modification
- if (iter->operation() == ChangeNotification::Add) {
+ if (it.operation() == Add) {
return false;
}
}
@@ -8582,31 +8940,1360 @@ bool ChangeNotification::appendAndCompress(ChangeNotification::List &list, const
return true;
}
-DataStream &operator<<(DataStream &stream, const Akonadi::Protocol::ChangeNotification::Entity &entity)
+
+
+class TagChangeNotificationPrivate : public ChangeNotificationPrivate
+{
+public:
+ TagChangeNotificationPrivate()
+ : ChangeNotificationPrivate(Command::TagChangeNotification)
+ , operation(TagChangeNotification::InvalidOp)
+ {}
+
+ TagChangeNotificationPrivate(const TagChangeNotificationPrivate &other)
+ : ChangeNotificationPrivate(other)
+ , resource(other.resource)
+ , remoteId(other.remoteId)
+ , id(other.id)
+ , operation(other.operation)
+ {}
+
+ bool compare(const CommandPrivate *other) const Q_DECL_OVERRIDE
+ {
+ return ChangeNotificationPrivate::compare(other)
+ && COMPARE(operation)
+ && COMPARE(id)
+ && COMPARE(remoteId)
+ && COMPARE(resource);
+ }
+
+ CommandPrivate *clone() const Q_DECL_OVERRIDE
+ {
+ return new TagChangeNotificationPrivate(*this);
+ }
+
+ DataStream &serialize(DataStream &stream) const Q_DECL_OVERRIDE
+ {
+ return ChangeNotificationPrivate::serialize(stream)
+ << operation
+ << id
+ << remoteId
+ << resource;
+ }
+
+ DataStream &deserialize(DataStream &stream) Q_DECL_OVERRIDE
+ {
+ return ChangeNotificationPrivate::deserialize(stream)
+ >> operation
+ >> id
+ >> remoteId
+ >> resource;
+ }
+
+ void debugString(DebugBlock &blck) const Q_DECL_OVERRIDE
+ {
+ blck.write("Operation", [this]() -> QString {
+ switch (operation) {
+ case TagChangeNotification::Add:
+ return QStringLiteral("Add");
+ case TagChangeNotification::Modify:
+ return QStringLiteral("Modify");
+ case TagChangeNotification::Remove:
+ return QStringLiteral("Remove");
+ case ItemChangeNotification::InvalidOp:
+ return QStringLiteral("*INVALID OPERATION*");
+ }
+ Q_ASSERT(false);
+ return QString();
+ }());
+ blck.write("Tag ID", id);
+ blck.write("Tag RID", remoteId);
+ blck.endBlock();
+ blck.write("Resource", resource);
+ }
+
+
+
+ QByteArray resource;
+ QString remoteId;
+ TagChangeNotification::Id id;
+ TagChangeNotification::Operation operation;
+};
+
+AKONADI_DECLARE_PRIVATE(TagChangeNotification)
+
+
+TagChangeNotification::TagChangeNotification()
+ : ChangeNotification(new TagChangeNotificationPrivate)
+{
+}
+
+TagChangeNotification::TagChangeNotification(const Command &other)
+ : ChangeNotification(other)
+{
+ checkCopyInvariant(Command::TagChangeNotification);
+}
+
+bool TagChangeNotification::isValid() const
+{
+ Q_D(const TagChangeNotification);
+ return d->commandType == Command::TagChangeNotification
+ && d->operation != InvalidOp;
+}
+
+void TagChangeNotification::setId(Id id)
{
- return stream << entity.id
- << entity.remoteId
- << entity.remoteRevision
- << entity.mimeType;
+ d_func()->id = id;
}
-DataStream &operator>>(DataStream &stream, Akonadi::Protocol::ChangeNotification::Entity &entity)
+TagChangeNotification::Id TagChangeNotification::id() const
{
- return stream >> entity.id
- >> entity.remoteId
- >> entity.remoteRevision
- >> entity.mimeType;
+ return d_func()->id;
+}
+
+void TagChangeNotification::setRemoteId(const QString &remoteId)
+{
+ d_func()->remoteId = remoteId;
+}
+
+QString TagChangeNotification::remoteId() const
+{
+ return d_func()->remoteId;
+}
+
+TagChangeNotification::Operation TagChangeNotification::operation() const
+{
+ return d_func()->operation;
+}
+
+void TagChangeNotification::setOperation(Operation operation)
+{
+ d_func()->operation = operation;
}
-DataStream &operator<<(DataStream &stream, const Akonadi::Protocol::ChangeNotification &command)
+QByteArray TagChangeNotification::resource() const
+{
+ return d_func()->resource;
+}
+
+void TagChangeNotification::setResource(const QByteArray &resource)
+{
+ d_func()->resource = resource;
+}
+
+DataStream &operator<<(DataStream &stream, const Akonadi::Protocol::TagChangeNotification &command)
{
return command.d_func()->serialize(stream);
}
-DataStream &operator>>(DataStream &stream, Akonadi::Protocol::ChangeNotification &command)
+DataStream &operator>>(DataStream &stream, Akonadi::Protocol::TagChangeNotification &command)
{
return command.d_func()->deserialize(stream);
}
+
+
+class RelationChangeNotificationPrivate : public ChangeNotificationPrivate
+{
+
+public:
+ RelationChangeNotificationPrivate()
+ : ChangeNotificationPrivate(Command::RelationChangeNotification)
+ , left(-1)
+ , right(-1)
+ , operation(RelationChangeNotification::InvalidOp)
+ {
+ }
+
+ RelationChangeNotificationPrivate(const RelationChangeNotificationPrivate &other)
+ : ChangeNotificationPrivate(other)
+ , left(other.left)
+ , right(other.right)
+ , remoteId(other.remoteId)
+ , type(other.type)
+ , operation(other.operation)
+ {
+ }
+
+ bool compare(const CommandPrivate *other) const Q_DECL_OVERRIDE
+ {
+ return ChangeNotificationPrivate::compare(other)
+ && COMPARE(operation)
+ && COMPARE(left)
+ && COMPARE(right)
+ && COMPARE(remoteId)
+ && COMPARE(type);
+ }
+
+ void debugString(DebugBlock &blck) const Q_DECL_OVERRIDE
+ {
+ blck.write("Operation", [this]() -> QString {
+ switch (operation) {
+ case RelationChangeNotification::Add:
+ return QStringLiteral("Add");
+ case RelationChangeNotification::Remove:
+ return QStringLiteral("Remove");
+ case RelationChangeNotification::InvalidOp:
+ return QStringLiteral("*INVALID OPERATION*");
+ }
+ Q_ASSERT(false);
+ return QString();
+ }());
+ blck.write("Left Item", left);
+ blck.write("Right Item", right);
+ blck.write("Remote ID", remoteId);
+ blck.write("Type", type);
+ }
+
+ DataStream &deserialize(DataStream &stream) Q_DECL_OVERRIDE
+ {
+ return ChangeNotificationPrivate::deserialize(stream)
+ >> operation
+ >> left
+ >> right
+ >> remoteId
+ >> type;
+ }
+
+ DataStream &serialize(DataStream &stream) const Q_DECL_OVERRIDE
+ {
+ return ChangeNotificationPrivate::serialize(stream)
+ << operation
+ << left
+ << right
+ << remoteId
+ << type;
+ }
+
+ CommandPrivate *clone() const Q_DECL_OVERRIDE
+ {
+ return new RelationChangeNotificationPrivate(*this);
+ }
+
+ RelationChangeNotification::Id left;
+ RelationChangeNotification::Id right;
+ QString remoteId;
+ QString type;
+ RelationChangeNotification::Operation operation;
+};
+
+AKONADI_DECLARE_PRIVATE(RelationChangeNotification)
+
+RelationChangeNotification::RelationChangeNotification()
+ : ChangeNotification(new RelationChangeNotificationPrivate)
+{
+}
+
+RelationChangeNotification::RelationChangeNotification(const Command &other)
+ : ChangeNotification(other)
+{
+ checkCopyInvariant(Command::RelationChangeNotification);
+}
+
+RelationChangeNotification::Operation RelationChangeNotification::operation() const
+{
+ return d_func()->operation;
+}
+
+void RelationChangeNotification::setOperation(Operation operation)
+{
+ d_func()->operation = operation;
+}
+
+RelationChangeNotification::Id RelationChangeNotification::leftItem() const
+{
+ return d_func()->left;
+}
+
+void RelationChangeNotification::setLeftItem(Id left)
+{
+ d_func()->left = left;
+}
+
+RelationChangeNotification::Id RelationChangeNotification::rightItem() const
+{
+ return d_func()->right;
+}
+
+void RelationChangeNotification::setRightItem(Id right)
+{
+ d_func()->right = right;
+}
+
+QString RelationChangeNotification::remoteId() const
+{
+ return d_func()->remoteId;
+}
+
+void RelationChangeNotification::setRemoteId(const QString &remoteId)
+{
+ d_func()->remoteId = remoteId;
+}
+
+QString RelationChangeNotification::type() const
+{
+ return d_func()->type;
+}
+
+void RelationChangeNotification::setType(const QString &type)
+{
+ d_func()->type = type;
+}
+
+
+DataStream &operator<<(DataStream &stream, const Akonadi::Protocol::RelationChangeNotification &command)
+{
+ return command.d_func()->serialize(stream);
+}
+
+DataStream &operator>>(DataStream &stream, Akonadi::Protocol::RelationChangeNotification &command)
+{
+ return command.d_func()->deserialize(stream);
+}
+
+class CreateSubscriptionCommandPrivate : public CommandPrivate
+{
+public:
+ CreateSubscriptionCommandPrivate(const QByteArray &subscriberName = QByteArray(),
+ const QByteArray &session = QByteArray())
+ : CommandPrivate(Command::CreateSubscription)
+ , subscriberName(subscriberName)
+ , session(session)
+ {}
+
+ bool compare(const CommandPrivate *other) const Q_DECL_OVERRIDE
+ {
+ return CommandPrivate::compare(other)
+ && COMPARE(subscriberName)
+ && COMPARE(session);
+ }
+
+ void debugString(DebugBlock &blck) const Q_DECL_OVERRIDE
+ {
+ blck.write("Subscriber", subscriberName);
+ blck.write("Session", session);
+ }
+
+ DataStream &deserialize(DataStream &stream) Q_DECL_OVERRIDE
+ {
+ return CommandPrivate::deserialize(stream)
+ >> subscriberName
+ >> session;
+ }
+
+ DataStream &serialize(DataStream &stream) const Q_DECL_OVERRIDE
+ {
+ return CommandPrivate::serialize(stream)
+ << subscriberName
+ << session;
+ }
+
+ CommandPrivate *clone() const Q_DECL_OVERRIDE
+ {
+ return new CreateSubscriptionCommandPrivate(subscriberName, session);
+ }
+
+ QByteArray subscriberName;
+ QByteArray session;
+};
+
+AKONADI_DECLARE_PRIVATE(CreateSubscriptionCommand)
+
+CreateSubscriptionCommand::CreateSubscriptionCommand()
+ : Command(new CreateSubscriptionCommandPrivate())
+{
+}
+
+CreateSubscriptionCommand::CreateSubscriptionCommand(const QByteArray &subscriberName,
+ const QByteArray &session)
+ : Command(new CreateSubscriptionCommandPrivate(subscriberName, session))
+{
+}
+
+CreateSubscriptionCommand::CreateSubscriptionCommand(const Command &other)
+ : Command(other)
+{
+ checkCopyInvariant(Command::CreateSubscription);
+}
+
+void CreateSubscriptionCommand::setSubscriberName(const QByteArray &subscriberName)
+{
+ d_func()->subscriberName = subscriberName;
+}
+
+QByteArray CreateSubscriptionCommand::subscriberName() const
+{
+ return d_func()->subscriberName;
+}
+
+void CreateSubscriptionCommand::setSession(const QByteArray &session)
+{
+ d_func()->session = session;
+}
+
+QByteArray CreateSubscriptionCommand::session() const
+{
+ return d_func()->session;
+}
+
+DataStream &operator<<(DataStream &stream, const CreateSubscriptionCommand &command)
+{
+ return command.d_func()->serialize(stream);
+}
+
+DataStream &operator>>(DataStream &stream, CreateSubscriptionCommand &command)
+{
+ return command.d_func()->deserialize(stream);
+}
+
+
+
+
+CreateSubscriptionResponse::CreateSubscriptionResponse()
+ : Response(new ResponsePrivate(Command::CreateSubscription))
+{
+}
+
+CreateSubscriptionResponse::CreateSubscriptionResponse(const Command &other)
+ : Response(other)
+{
+ checkCopyInvariant(Command::CreateSubscription);
+}
+
+
+
+
+class ModifySubscriptionCommandPrivate : public CommandPrivate
+{
+public:
+ explicit ModifySubscriptionCommandPrivate(const QByteArray &subscriberName = QByteArray())
+ : CommandPrivate(Command::ModifySubscription)
+ , subscriberName(subscriberName)
+ , modifiedParts(ModifySubscriptionCommand::None)
+ , monitorAll(false)
+ , exclusive(false)
+ {
+ }
+
+ ModifySubscriptionCommandPrivate(const ModifySubscriptionCommandPrivate &other)
+ : CommandPrivate(other)
+ , subscriberName(other.subscriberName)
+ , modifiedParts(other.modifiedParts)
+ , startCollections(other.startCollections)
+ , stopCollections(other.stopCollections)
+ , startItems(other.startItems)
+ , stopItems(other.stopItems)
+ , startTags(other.startTags)
+ , stopTags(other.stopTags)
+ , startTypes(other.startTypes)
+ , stopTypes(other.stopTypes)
+ , startResources(other.startResources)
+ , stopResources(other.stopResources)
+ , startMimeTypes(other.startMimeTypes)
+ , stopMimeTypes(other.stopMimeTypes)
+ , startSessions(other.startSessions)
+ , stopSessions(other.stopSessions)
+ , monitorAll(other.monitorAll)
+ , exclusive(other.exclusive)
+ {
+ }
+
+ bool compare(const CommandPrivate *other) const Q_DECL_OVERRIDE
+ {
+ return CommandPrivate::compare(other)
+ && COMPARE(subscriberName)
+ && COMPARE(modifiedParts)
+ && COMPARE(startCollections)
+ && COMPARE(stopCollections)
+ && COMPARE(startItems)
+ && COMPARE(stopItems)
+ && COMPARE(startTags)
+ && COMPARE(stopTags)
+ && COMPARE(startTypes)
+ && COMPARE(stopTypes)
+ && COMPARE(startResources)
+ && COMPARE(stopResources)
+ && COMPARE(startMimeTypes)
+ && COMPARE(stopMimeTypes)
+ && COMPARE(startSessions)
+ && COMPARE(stopSessions)
+ && COMPARE(monitorAll)
+ && COMPARE(exclusive);
+ }
+
+ void debugString(DebugBlock &blck) const Q_DECL_OVERRIDE
+ {
+ blck.write("Subscriber", subscriberName);
+ // TODO: Expand the flags
+ blck.write("Modified parts", modifiedParts);
+ if (modifiedParts & (ModifySubscriptionCommand::Collections | ModifySubscriptionCommand::Add)) {
+ blck.write("Start Collections", startCollections);
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Collections | ModifySubscriptionCommand::Remove)) {
+ blck.write("Stop Collections", stopCollections);
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Items | ModifySubscriptionCommand::Add)) {
+ blck.write("Start Items", startItems);
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Items | ModifySubscriptionCommand::Remove)) {
+ blck.write("Stop Items", stopItems);
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Tags | ModifySubscriptionCommand::Add)) {
+ blck.write("Start Tags", startTags);
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Tags | ModifySubscriptionCommand::Remove)) {
+ blck.write("Stop Tags", stopTags);
+ }
+ // TODO: Expand types
+ if (modifiedParts & (ModifySubscriptionCommand::Types | ModifySubscriptionCommand::Add)) {
+ blck.write("Start types", startTypes);
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Types | ModifySubscriptionCommand::Remove)) {
+ blck.write("Stop types", stopTypes);
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Resources | ModifySubscriptionCommand::Add)) {
+ blck.write("Start resources", startResources);
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Resources | ModifySubscriptionCommand::Remove)) {
+ blck.write("Stop resources", stopResources);
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::MimeTypes | ModifySubscriptionCommand::Add)) {
+ blck.write("Start mimetypes", startMimeTypes);
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::MimeTypes | ModifySubscriptionCommand::Remove)) {
+ blck.write("Stop mimetypes", stopMimeTypes);
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Sessions | ModifySubscriptionCommand::Add)) {
+ blck.write("Start sessions", startSessions);
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Sessions | ModifySubscriptionCommand::Remove)) {
+ blck.write("Stop sessions", stopSessions);
+ }
+ if (modifiedParts & ModifySubscriptionCommand::AllFlag) {
+ blck.write("Monitor All", monitorAll);
+ }
+ if (modifiedParts & ModifySubscriptionCommand::ExclusiveFlag) {
+ blck.write("Exclusive", exclusive);
+ }
+ }
+
+ DataStream &serialize(DataStream &stream) const Q_DECL_OVERRIDE
+ {
+ CommandPrivate::serialize(stream)
+ << subscriberName
+ << modifiedParts;
+ if (modifiedParts & (ModifySubscriptionCommand::Collections | ModifySubscriptionCommand::Add)) {
+ stream << startCollections;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Collections | ModifySubscriptionCommand::Remove)) {
+ stream << stopCollections;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Items | ModifySubscriptionCommand::Add)) {
+ stream << startItems;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Items | ModifySubscriptionCommand::Remove)) {
+ stream << stopItems;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Tags | ModifySubscriptionCommand::Add)) {
+ stream << startTags;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Tags | ModifySubscriptionCommand::Remove)) {
+ stream << stopTags;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Types | ModifySubscriptionCommand::Add)) {
+ stream << startTypes;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Types | ModifySubscriptionCommand::Remove)) {
+ stream << stopTypes;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Resources | ModifySubscriptionCommand::Add)) {
+ stream << startResources;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Resources | ModifySubscriptionCommand::Remove)) {
+ stream << stopResources;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::MimeTypes | ModifySubscriptionCommand::Add)) {
+ stream << startMimeTypes;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::MimeTypes | ModifySubscriptionCommand::Remove)) {
+ stream << stopMimeTypes;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Sessions | ModifySubscriptionCommand::Add)) {
+ stream << startSessions;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Sessions | ModifySubscriptionCommand::Remove)) {
+ stream << stopSessions;
+ }
+ if (modifiedParts & ModifySubscriptionCommand::AllFlag) {
+ stream << monitorAll;
+ }
+ if (modifiedParts & ModifySubscriptionCommand::ExclusiveFlag) {
+ stream << exclusive;
+ }
+ return stream;
+ }
+
+ DataStream &deserialize(DataStream &stream) Q_DECL_OVERRIDE
+ {
+ CommandPrivate::deserialize(stream)
+ >> subscriberName
+ >> modifiedParts;
+ if (modifiedParts & (ModifySubscriptionCommand::Collections | ModifySubscriptionCommand::Add)) {
+ stream >> startCollections;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Collections | ModifySubscriptionCommand::Remove)) {
+ stream >> stopCollections;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Items | ModifySubscriptionCommand::Add)) {
+ stream >> startItems;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Items | ModifySubscriptionCommand::Remove)) {
+ stream >> stopItems;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Tags | ModifySubscriptionCommand::Add)) {
+ stream >> startTags;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Tags | ModifySubscriptionCommand::Remove)) {
+ stream >> stopTags;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Types | ModifySubscriptionCommand::Add)) {
+ stream >> startTypes;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Types | ModifySubscriptionCommand::Remove)) {
+ stream >> stopTypes;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Resources | ModifySubscriptionCommand::Add)) {
+ stream >> startResources;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Resources | ModifySubscriptionCommand::Remove)) {
+ stream >> stopResources;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::MimeTypes | ModifySubscriptionCommand::Add)) {
+ stream >> startMimeTypes;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::MimeTypes | ModifySubscriptionCommand::Remove)) {
+ stream >> stopMimeTypes;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Sessions | ModifySubscriptionCommand::Add)) {
+ stream >> startSessions;
+ }
+ if (modifiedParts & (ModifySubscriptionCommand::Sessions | ModifySubscriptionCommand::Remove)) {
+ stream >> stopSessions;
+ }
+ if (modifiedParts & ModifySubscriptionCommand::AllFlag) {
+ stream >> monitorAll;
+ }
+ if (modifiedParts & ModifySubscriptionCommand::ExclusiveFlag) {
+ stream >> exclusive;
+ }
+ return stream;
+ }
+
+ CommandPrivate *clone() const Q_DECL_OVERRIDE
+ {
+ return new ModifySubscriptionCommandPrivate(*this);
+ }
+
+ QByteArray subscriberName;
+ ModifySubscriptionCommand::ModifiedParts modifiedParts;
+ // TODO: Don't waste so much space!
+ QVector<qint64> startCollections;
+ QVector<qint64> stopCollections;
+ QVector<qint64> startItems;
+ QVector<qint64> stopItems;
+ QVector<qint64> startTags;
+ QVector<qint64> stopTags;
+ QVector<ModifySubscriptionCommand::ChangeType> startTypes;
+ QVector<ModifySubscriptionCommand::ChangeType> stopTypes;
+ QVector<QByteArray> startResources;
+ QVector<QByteArray> stopResources;
+ QStringList startMimeTypes;
+ QStringList stopMimeTypes;
+ QVector<QByteArray> startSessions;
+ QVector<QByteArray> stopSessions;
+ bool monitorAll;
+ bool exclusive;
+};
+
+AKONADI_DECLARE_PRIVATE(ModifySubscriptionCommand)
+
+ModifySubscriptionCommand::ModifySubscriptionCommand()
+ : Command(new ModifySubscriptionCommandPrivate())
+{
+}
+
+ModifySubscriptionCommand::ModifySubscriptionCommand(const Command &other)
+ : Command(other)
+{
+ checkCopyInvariant(Command::ModifySubscription);
+}
+
+void ModifySubscriptionCommand::setSubscriberName(const QByteArray &subscriberName)
+{
+ d_func()->subscriberName = subscriberName;
+}
+
+QByteArray ModifySubscriptionCommand::subscriberName() const
+{
+ return d_func()->subscriberName;
+}
+
+ModifySubscriptionCommand::ModifiedParts ModifySubscriptionCommand::modifiedParts() const
+{
+ return d_func()->modifiedParts;
+}
+
+void ModifySubscriptionCommand::startMonitoringCollection(qint64 id)
+{
+ d_func()->stopCollections.removeAll(id);
+ d_func()->startCollections << id;
+ d_func()->modifiedParts |= ModifiedParts(Collections | Add);
+}
+
+QVector<qint64> ModifySubscriptionCommand::startMonitoringCollections() const
+{
+ return d_func()->startCollections;
+}
+
+void ModifySubscriptionCommand::stopMonitoringCollection(qint64 id)
+{
+ d_func()->startCollections.removeAll(id);
+ d_func()->stopCollections << id;
+ d_func()->modifiedParts |= ModifiedParts(Collections | Remove);
+}
+
+QVector<qint64> ModifySubscriptionCommand::stopMonitoringCollections() const
+{
+ return d_func()->stopCollections;
+}
+
+void ModifySubscriptionCommand::startMonitoringItem(qint64 id)
+{
+ d_func()->stopItems.removeAll(id);
+ d_func()->startItems << id;
+ d_func()->modifiedParts |= ModifiedParts(Items | Add);
+}
+
+QVector<qint64> ModifySubscriptionCommand::startMonitoringItems() const
+{
+ return d_func()->startItems;
+}
+
+void ModifySubscriptionCommand::stopMonitoringItem(qint64 id)
+{
+ d_func()->startItems.removeAll(id);
+ d_func()->stopItems << id;
+ d_func()->modifiedParts |= ModifiedParts(Items | Remove);
+}
+
+QVector<qint64> ModifySubscriptionCommand::stopMonitoringItems() const
+{
+ return d_func()->stopItems;
+}
+
+void ModifySubscriptionCommand::startMonitoringTag(qint64 id)
+{
+ d_func()->stopTags.removeAll(id);
+ d_func()->startTags << id;
+ d_func()->modifiedParts |= ModifiedParts(Tags | Add);
+}
+
+QVector<qint64> ModifySubscriptionCommand::startMonitoringTags() const
+{
+ return d_func()->startTags;
+}
+
+void ModifySubscriptionCommand::stopMonitoringTag(qint64 id)
+{
+ d_func()->startTags.removeAll(id);
+ d_func()->stopTags << id;
+ d_func()->modifiedParts |= ModifiedParts(Tags | Remove);
+}
+
+QVector<qint64> ModifySubscriptionCommand::stopMonitoringTags() const
+{
+ return d_func()->stopTags;
+}
+
+void ModifySubscriptionCommand::startMonitoringType(ChangeType type)
+{
+ d_func()->stopTypes.removeAll(type);
+ d_func()->startTypes << type;
+ d_func()->modifiedParts |= ModifiedParts(Types | Add);
+}
+
+QVector<ModifySubscriptionCommand::ChangeType> ModifySubscriptionCommand::startMonitoringTypes() const
+{
+ return d_func()->startTypes;
+}
+
+void ModifySubscriptionCommand::stopMonitoringType(ChangeType type)
+{
+ d_func()->startTypes.removeAll(type);
+ d_func()->stopTypes << type;
+ d_func()->modifiedParts |= ModifiedParts(Types | Remove);
+}
+
+QVector<ModifySubscriptionCommand::ChangeType> ModifySubscriptionCommand::stopMonitoringTypes() const
+{
+ return d_func()->stopTypes;
+}
+
+void ModifySubscriptionCommand::startMonitoringResource(const QByteArray &resource)
+{
+ d_func()->stopResources.removeAll(resource);
+ d_func()->startResources << resource;
+ d_func()->modifiedParts |= ModifiedParts(Resources | Add);
+}
+
+QVector<QByteArray> ModifySubscriptionCommand::startMonitoringResources() const
+{
+ return d_func()->startResources;
+}
+
+void ModifySubscriptionCommand::stopMonitoringResource(const QByteArray &resource)
+{
+ d_func()->startResources.removeAll(resource);
+ d_func()->stopResources << resource;
+ d_func()->modifiedParts |= ModifiedParts(Resources | Remove);
+}
+
+QVector<QByteArray> ModifySubscriptionCommand::stopMonitoringResources() const
+{
+ return d_func()->stopResources;
+}
+
+void ModifySubscriptionCommand::startMonitoringMimeType(const QString &mimeType)
+{
+ d_func()->stopMimeTypes.removeAll(mimeType);
+ d_func()->startMimeTypes << mimeType;
+ d_func()->modifiedParts |= ModifiedParts(MimeTypes | Add);
+}
+
+QStringList ModifySubscriptionCommand::startMonitoringMimeTypes() const
+{
+ return d_func()->startMimeTypes;
+}
+
+void ModifySubscriptionCommand::stopMonitoringMimeType(const QString &mimeType)
+{
+ d_func()->startMimeTypes.removeAll(mimeType);
+ d_func()->stopMimeTypes << mimeType;
+ d_func()->modifiedParts |= ModifiedParts(MimeTypes | Remove);
+}
+
+QStringList ModifySubscriptionCommand::stopMonitoringMimeTypes() const
+{
+ return d_func()->stopMimeTypes;
+}
+
+void ModifySubscriptionCommand::setAllMonitored(bool allMonitored)
+{
+ d_func()->monitorAll = allMonitored;
+ d_func()->modifiedParts |= AllFlag;
+}
+
+bool ModifySubscriptionCommand::allMonitored() const
+{
+ return d_func()->monitorAll;
+}
+
+void ModifySubscriptionCommand::setExclusive(bool exclusive)
+{
+ d_func()->exclusive = exclusive;
+ d_func()->modifiedParts |= ExclusiveFlag;
+}
+
+bool ModifySubscriptionCommand::isExclusive() const
+{
+ return d_func()->exclusive;
+}
+
+void ModifySubscriptionCommand::startIgnoringSession(const QByteArray &session)
+{
+ d_func()->stopSessions.removeAll(session);
+ d_func()->startSessions << session;
+ d_func()->modifiedParts |= ModifiedParts(Sessions | Add);
+}
+
+QVector<QByteArray> ModifySubscriptionCommand::startIgnoringSessions() const
+{
+ return d_func()->startSessions;
+}
+
+void ModifySubscriptionCommand::stopIgnoringSession(const QByteArray &session)
+{
+ d_func()->startSessions.removeAll(session);
+ d_func()->stopSessions << session;
+ d_func()->modifiedParts |= ModifiedParts(Sessions | Remove);
+}
+
+QVector<QByteArray> ModifySubscriptionCommand::stopIgnoringSessions() const
+{
+ return d_func()->stopSessions;
+}
+
+DataStream &operator<<(DataStream &stream, const ModifySubscriptionCommand &command)
+{
+ return command.d_ptr->serialize(stream);
+}
+
+DataStream &operator>>(DataStream &stream, ModifySubscriptionCommand &command)
+{
+ return command.d_ptr->deserialize(stream);
+}
+
+
+
+ModifySubscriptionResponse::ModifySubscriptionResponse()
+ : Response(new ResponsePrivate(Command::ModifySubscription))
+{
+}
+
+ModifySubscriptionResponse::ModifySubscriptionResponse(const Command &other)
+ : Response(other)
+{
+ checkCopyInvariant(Command::ModifySubscription);
+}
+
+
+
+class SubscriptionChangeNotificationPrivate : public ChangeNotificationPrivate
+{
+public:
+ SubscriptionChangeNotificationPrivate()
+ : ChangeNotificationPrivate(Command::SubscriptionChangeNotification)
+ , modifiedParts(SubscriptionChangeNotification::None)
+ , operation(SubscriptionChangeNotification::InvalidOp)
+ , isAllMonitored(false)
+ , isExclusive(false)
+ {}
+
+ SubscriptionChangeNotificationPrivate(const SubscriptionChangeNotificationPrivate &other)
+ : ChangeNotificationPrivate(other)
+ , subscriber(other.subscriber)
+ , addedCollections(other.addedCollections)
+ , removedCollections(other.removedCollections)
+ , addedItems(other.addedItems)
+ , removedItems(other.removedItems)
+ , addedTags(other.addedTags)
+ , removedTags(other.removedTags)
+ , addedTypes(other.addedTypes)
+ , removedTypes(other.removedTypes)
+ , addedMimeTypes(other.addedMimeTypes)
+ , removedMimeTypes(other.removedMimeTypes)
+ , addedResources(other.addedResources)
+ , removedResources(other.removedResources)
+ , addedIgnoredSessions(other.addedIgnoredSessions)
+ , removedIgnoredSessions(other.removedIgnoredSessions)
+ , modifiedParts(other.modifiedParts)
+ , operation(other.operation)
+ , isAllMonitored(other.isAllMonitored)
+ , isExclusive(other.isExclusive)
+ {}
+
+ bool compare(const CommandPrivate *other) const Q_DECL_OVERRIDE
+ {
+ return ChangeNotificationPrivate::compare(other)
+ && COMPARE(modifiedParts)
+ && COMPARE(operation)
+ && COMPARE(subscriber)
+ && COMPARE(addedCollections)
+ && COMPARE(removedCollections)
+ && COMPARE(addedItems)
+ && COMPARE(removedItems)
+ && COMPARE(addedTags)
+ && COMPARE(removedTags)
+ && COMPARE(addedTypes)
+ && COMPARE(removedTypes)
+ && COMPARE(addedMimeTypes)
+ && COMPARE(removedMimeTypes)
+ && COMPARE(addedResources)
+ && COMPARE(removedResources)
+ && COMPARE(addedIgnoredSessions)
+ && COMPARE(removedIgnoredSessions)
+ && COMPARE(isAllMonitored)
+ && COMPARE(isExclusive);
+ }
+
+ void debugString(DebugBlock &blck) const Q_DECL_OVERRIDE
+ {
+ ChangeNotificationPrivate::debugString(blck);
+ blck.write("Subscriber", subscriber);
+ blck.write("Operation", operation);
+ blck.write("Modified parts", modifiedParts);
+ if (modifiedParts & SubscriptionChangeNotification::Collections) {
+ blck.write("Added cols", addedCollections);
+ blck.write("Removed cols", removedCollections);
+ }
+ if (modifiedParts & SubscriptionChangeNotification::Items) {
+ blck.write("Added items", addedItems);
+ blck.write("Removed items", removedItems);
+ }
+ if (modifiedParts & SubscriptionChangeNotification::Tags) {
+ blck.write("Added tags", addedTags);
+ blck.write("Removed tags", removedTags);
+ }
+ if (modifiedParts & SubscriptionChangeNotification::Types) {
+ blck.write("Added types", addedTypes);
+ blck.write("Removed types", removedTypes);
+ }
+ if (modifiedParts & SubscriptionChangeNotification::MimeTypes) {
+ blck.write("Added mimetypes", addedMimeTypes);
+ blck.write("Removed mimetypes", removedMimeTypes);
+ }
+ if (modifiedParts & SubscriptionChangeNotification::Resources) {
+ blck.write("Added resources", addedResources);
+ blck.write("Removed resources", removedResources);
+ }
+ if (modifiedParts & SubscriptionChangeNotification::IgnoredSessions) {
+ blck.write("Added ignored sessions", addedIgnoredSessions);
+ blck.write("Removed ignored sessions", removedIgnoredSessions);
+ }
+ if (modifiedParts & SubscriptionChangeNotification::Monitored) {
+ blck.write("All monitored", isAllMonitored);
+ }
+ if (modifiedParts & SubscriptionChangeNotification::Exclusive) {
+ blck.write("Is exclusive", isExclusive);
+ }
+ }
+
+ DataStream &serialize(DataStream &stream) const Q_DECL_OVERRIDE
+ {
+ ChangeNotificationPrivate::serialize(stream)
+ << subscriber
+ << operation
+ << modifiedParts;
+ if (modifiedParts & SubscriptionChangeNotification::Collections) {
+ stream << addedCollections
+ << removedCollections;
+ }
+ if (modifiedParts & SubscriptionChangeNotification::Items) {
+ stream << addedItems
+ << removedItems;
+ }
+ if (modifiedParts & SubscriptionChangeNotification::Tags) {
+ stream << addedTags
+ << removedTags;
+ }
+ if (modifiedParts & SubscriptionChangeNotification::Types) {
+ stream << addedTypes
+ << removedTypes;
+ }
+ if (modifiedParts & SubscriptionChangeNotification::MimeTypes) {
+ stream << addedMimeTypes
+ << removedMimeTypes;
+ }
+ if (modifiedParts & SubscriptionChangeNotification::Resources) {
+ stream << addedResources
+ << removedResources;
+ }
+ if (modifiedParts & SubscriptionChangeNotification::IgnoredSessions) {
+ stream << addedIgnoredSessions
+ << removedIgnoredSessions;
+ }
+ if (modifiedParts & SubscriptionChangeNotification::Monitored) {
+ stream << isAllMonitored;
+ }
+ if (modifiedParts & SubscriptionChangeNotification::Exclusive) {
+ stream << isExclusive;
+ }
+ return stream;
+ }
+
+ DataStream &deserialize(DataStream &stream) Q_DECL_OVERRIDE
+ {
+ ChangeNotificationPrivate::deserialize(stream)
+ >> subscriber
+ >> operation
+ >> modifiedParts;
+ if (modifiedParts & SubscriptionChangeNotification::Collections) {
+ stream >> addedCollections
+ >> removedCollections;
+ }
+ if (modifiedParts & SubscriptionChangeNotification::Items) {
+ stream >> addedItems
+ >> removedItems;
+ }
+ if (modifiedParts & SubscriptionChangeNotification::Tags) {
+ stream >> addedTags
+ >> removedTags;
+ }
+ if (modifiedParts & SubscriptionChangeNotification::Types) {
+ stream >> addedTypes
+ >> removedTypes;
+ }
+ if (modifiedParts & SubscriptionChangeNotification::MimeTypes) {
+ stream >> addedMimeTypes
+ >> removedMimeTypes;
+ }
+ if (modifiedParts & SubscriptionChangeNotification::Resources) {
+ stream >> addedResources
+ >> removedResources;
+ }
+ if (modifiedParts & SubscriptionChangeNotification::IgnoredSessions) {
+ stream >> addedIgnoredSessions
+ >> removedIgnoredSessions;
+ }
+ if (modifiedParts & SubscriptionChangeNotification::Monitored) {
+ stream >> isAllMonitored;
+ }
+ if (modifiedParts & SubscriptionChangeNotification::Exclusive) {
+ stream >> isExclusive;
+ }
+ return stream;
+ }
+
+ CommandPrivate *clone() const Q_DECL_OVERRIDE
+ {
+ return new SubscriptionChangeNotificationPrivate(*this);
+ }
+
+ QByteArray subscriber;
+ QSet<ChangeNotification::Id> addedCollections;
+ QSet<ChangeNotification::Id> removedCollections;
+ QSet<ChangeNotification::Id> addedItems;
+ QSet<ChangeNotification::Id> removedItems;
+ QSet<ChangeNotification::Id> addedTags;
+ QSet<ChangeNotification::Id> removedTags;
+ QSet<ModifySubscriptionCommand::ChangeType> addedTypes;
+ QSet<ModifySubscriptionCommand::ChangeType> removedTypes;
+ QSet<QString> addedMimeTypes;
+ QSet<QString> removedMimeTypes;
+ QSet<QByteArray> addedResources;
+ QSet<QByteArray> removedResources;
+ QSet<QByteArray> addedIgnoredSessions;
+ QSet<QByteArray> removedIgnoredSessions;
+ SubscriptionChangeNotification::ModifiedParts modifiedParts;
+ SubscriptionChangeNotification::Operation operation;
+ bool isAllMonitored;
+ bool isExclusive;
+};
+
+AKONADI_DECLARE_PRIVATE(SubscriptionChangeNotification)
+
+SubscriptionChangeNotification::SubscriptionChangeNotification()
+ : ChangeNotification(new SubscriptionChangeNotificationPrivate())
+{
+}
+
+SubscriptionChangeNotification::SubscriptionChangeNotification(const Command &other)
+ : ChangeNotification(other)
+{
+ checkCopyInvariant(Command::SubscriptionChangeNotification);
+}
+
+SubscriptionChangeNotification::Operation SubscriptionChangeNotification::operation() const
+{
+ return d_func()->operation;
+}
+
+void SubscriptionChangeNotification::setOperation(Operation operation)
+{
+ d_func()->operation = operation;
+}
+
+SubscriptionChangeNotification::ModifiedParts SubscriptionChangeNotification::modifiedParts() const
+{
+ return d_func()->modifiedParts;
+}
+
+QByteArray SubscriptionChangeNotification::subscriber() const
+{
+ return d_func()->subscriber;
+}
+
+void SubscriptionChangeNotification::setSubscriber(const QByteArray &subscriber)
+{
+ d_func()->subscriber = subscriber;
+}
+
+QSet<ChangeNotification::Id> SubscriptionChangeNotification::addedCollections() const
+{
+ return d_func()->addedCollections;
+}
+
+void SubscriptionChangeNotification::setAddedCollections(const QSet<Id> &addedCols)
+{
+ d_func()->addedCollections = addedCols;
+ d_func()->modifiedParts |= Collections;
+}
+
+QSet<ChangeNotification::Id> SubscriptionChangeNotification::removedCollections() const
+{
+ return d_func()->removedCollections;
+}
+
+void SubscriptionChangeNotification::setRemovedCollections(const QSet<Id> &removedCols)
+{
+ d_func()->removedCollections = removedCols;
+ d_func()->modifiedParts|= Collections;
+}
+
+QSet<ChangeNotification::Id> SubscriptionChangeNotification::addedItems() const
+{
+ return d_func()->addedItems;
+}
+
+void SubscriptionChangeNotification::setAddedItems(const QSet<Id> &addedItems)
+{
+ d_func()->addedItems = addedItems;
+ d_func()->modifiedParts |= Items;
+}
+
+QSet<ChangeNotification::Id> SubscriptionChangeNotification::removedItems() const
+{
+ return d_func()->removedItems;
+}
+
+void SubscriptionChangeNotification::setRemovedItems(const QSet<Id> &removedItems)
+{
+ d_func()->removedItems = removedItems;
+ d_func()->modifiedParts |= Items;
+}
+
+QSet<ChangeNotification::Id> SubscriptionChangeNotification::addedTags() const
+{
+ return d_func()->addedTags;
+}
+
+void SubscriptionChangeNotification::setAddedTags(const QSet<Id> &addedTags)
+{
+ d_func()->addedTags = addedTags;
+ d_func()->modifiedParts |= Tags;
+}
+
+QSet<ChangeNotification::Id> SubscriptionChangeNotification::removedTags() const
+{
+ return d_func()->removedTags;
+}
+
+void SubscriptionChangeNotification::setRemovedTags(const QSet<Id> &removedTags)
+{
+ d_func()->removedTags = removedTags;
+ d_func()->modifiedParts |= Tags;
+}
+
+QSet<ModifySubscriptionCommand::ChangeType> SubscriptionChangeNotification::addedTypes() const
+{
+ return d_func()->addedTypes;
+}
+
+void SubscriptionChangeNotification::setAddedTypes(const QSet<ModifySubscriptionCommand::ChangeType> &addedTypes)
+{
+ d_func()->addedTypes = addedTypes;
+ d_func()->modifiedParts |= Types;
+}
+
+QSet<ModifySubscriptionCommand::ChangeType> SubscriptionChangeNotification::removedTypes() const
+{
+ return d_func()->removedTypes;
+}
+
+void SubscriptionChangeNotification::setRemovedTypes(const QSet<ModifySubscriptionCommand::ChangeType> &removedTypes)
+{
+ d_func()->removedTypes = removedTypes;
+ d_func()->modifiedParts |= Types;
+}
+
+QSet<QString> SubscriptionChangeNotification::addedMimeTypes() const
+{
+ return d_func()->addedMimeTypes;
+}
+
+void SubscriptionChangeNotification::setAddedMimeTypes(const QSet<QString> &addedMt)
+{
+ d_func()->addedMimeTypes = addedMt;
+ d_func()->modifiedParts |= MimeTypes;
+}
+
+QSet<QString> SubscriptionChangeNotification::removedMimeTypes() const
+{
+ return d_func()->removedMimeTypes;
+}
+
+void SubscriptionChangeNotification::setRemovedMimeTypes(const QSet<QString> &removedMt)
+{
+ d_func()->removedMimeTypes = removedMt;
+ d_func()->modifiedParts |= MimeTypes;
+}
+
+QSet<QByteArray> SubscriptionChangeNotification::addedResources() const
+{
+ return d_func()->addedResources;
+}
+
+void SubscriptionChangeNotification::setAddedResources(const QSet<QByteArray> &addedRes)
+{
+ d_func()->addedResources = addedRes;
+ d_func()->modifiedParts |= Resources;
+}
+
+QSet<QByteArray> SubscriptionChangeNotification::removedResources() const
+{
+ return d_func()->removedResources;
+}
+
+void SubscriptionChangeNotification::setRemovedResources(const QSet<QByteArray> &removedRes)
+{
+ d_func()->removedResources = removedRes;
+ d_func()->modifiedParts |= Resources;
+}
+
+QSet<QByteArray> SubscriptionChangeNotification::addedIgnoredSessions() const
+{
+ return d_func()->addedIgnoredSessions;
+}
+
+void SubscriptionChangeNotification::setAddedIgnoredSessions(const QSet<QByteArray> &addedSessions)
+{
+ d_func()->addedIgnoredSessions = addedSessions;
+ d_func()->modifiedParts |= IgnoredSessions;
+}
+
+QSet<QByteArray> SubscriptionChangeNotification::removedIgnoredSessions() const
+{
+ return d_func()->removedIgnoredSessions;
+}
+
+void SubscriptionChangeNotification::setRemovedIgnoredSessions(const QSet<QByteArray> &removedSessions)
+{
+ d_func()->removedIgnoredSessions = removedSessions;
+ d_func()->modifiedParts |= IgnoredSessions;
+}
+
+bool SubscriptionChangeNotification::isAllMonitored() const
+{
+ return d_func()->isAllMonitored;
+}
+
+void SubscriptionChangeNotification::setAllMonitored(bool allMonitored)
+{
+ d_func()->isAllMonitored = allMonitored;
+ d_func()->modifiedParts |= Monitored;
+}
+
+bool SubscriptionChangeNotification::isExclusive() const
+{
+ return d_func()->isExclusive;
+}
+
+void SubscriptionChangeNotification::setExclusive(bool isExclusive)
+{
+ d_func()->isExclusive = isExclusive;
+ d_func()->modifiedParts |= Exclusive;
+}
+
+DataStream &operator<<(DataStream &stream, const SubscriptionChangeNotification &ntf)
+{
+ return ntf.d_func()->serialize(stream);
+}
+
+DataStream &operator>>(DataStream &stream, SubscriptionChangeNotification &ntf)
+{
+ return ntf.d_func()->deserialize(stream);
+}
+
+
} // namespace Protocol
} // namespace Akonadi
diff --git a/src/private/protocol_p.h b/src/private/protocol_p.h
index 8d9197a..9f719cf 100644
--- a/src/private/protocol_p.h
+++ b/src/private/protocol_p.h
@@ -129,7 +129,15 @@ public:
// Other
StreamPayload = 100,
- ChangeNotification,
+
+ // Notifications
+ ItemChangeNotification = 110,
+ CollectionChangeNotification,
+ TagChangeNotification,
+ RelationChangeNotification,
+ SubscriptionChangeNotification,
+ CreateSubscription,
+ ModifySubscription,
_ResponseBit = 0x80 // reserved
};
@@ -492,21 +500,13 @@ class LoginCommandPrivate;
class AKONADIPRIVATE_EXPORT LoginCommand : public Command
{
public:
- enum SessionMode : uchar {
- CommandMode = 0,
- NotificationBus
- };
-
explicit LoginCommand();
- explicit LoginCommand(const QByteArray &sessionId, SessionMode mode = CommandMode);
+ explicit LoginCommand(const QByteArray &sessionId);
LoginCommand(const Command &command);
void setSessionId(const QByteArray &sessionId);
QByteArray sessionId() const;
- void setSessionMode(SessionMode mode);
- SessionMode sessionMode() const;
-
private:
AKONADI_DECLARE_PRIVATE(LoginCommand)
@@ -2012,22 +2012,40 @@ private:
};
-
-
class ChangeNotificationPrivate;
class AKONADIPRIVATE_EXPORT ChangeNotification : public Command
{
public:
- typedef QVector<ChangeNotification> List;
typedef qint64 Id;
+ typedef QVector<ChangeNotification> List;
- enum Type : uchar {
- InvalidType,
- Collections,
- Items,
- Tags,
- Relations
- };
+ ChangeNotification();
+ ChangeNotification(const Command &other);
+
+ bool isRemove() const;
+ bool isMove() const;
+
+ QByteArray sessionId() const;
+ void setSessionId(const QByteArray &sessionId);
+
+ void addMetadata(const QByteArray &metadata);
+ void removeMetadata(const QByteArray &metadata);
+ QVector<QByteArray> metadata() const;
+
+protected:
+ ChangeNotification(ChangeNotificationPrivate *dptr);
+
+private:
+ AKONADI_DECLARE_PRIVATE(ChangeNotification)
+};
+
+
+
+class ItemChangeNotificationPrivate;
+class AKONADIPRIVATE_EXPORT ItemChangeNotification : public ChangeNotification
+{
+public:
+ typedef QVector<ItemChangeNotification> List;
enum Operation : uchar {
InvalidOp,
@@ -2037,28 +2055,26 @@ public:
Remove,
Link,
Unlink,
- Subscribe,
- Unsubscribe,
ModifyFlags,
ModifyTags,
ModifyRelations
};
- class Entity
+ class Item
{
public:
- Entity()
+ Item()
: id(-1)
{}
- Entity(Id id, const QString &remoteId, const QString &remoteRevision, const QString &mimeType)
+ Item(Id id, const QString &remoteId, const QString &remoteRevision, const QString &mimeType)
: id(id)
, remoteId(remoteId)
, remoteRevision(remoteRevision)
, mimeType(mimeType)
{}
- bool operator==(const Entity &other) const
+ bool operator==(const Item &other) const
{
return id == other.id
&& remoteId == other.remoteId
@@ -2072,26 +2088,48 @@ public:
QString mimeType;
};
- explicit ChangeNotification();
- ChangeNotification(const Command& other);
+ class Relation
+ {
+ public:
+ Relation()
+ : leftId(-1)
+ , rightId(-1)
+ {
+ }
- bool isValid() const;
+ Relation(qint64 leftId, qint64 rightId, const QString &type)
+ : leftId(leftId)
+ , rightId(rightId)
+ , type(type)
+ {
+ }
- ChangeNotification::Type type() const;
- void setType(ChangeNotification::Type type);
+ bool operator==(const Relation &other) const
+ {
+ return leftId == other.leftId
+ && rightId == other.rightId
+ && type == other.type;
+ }
- ChangeNotification::Operation operation() const;
- void setOperation(ChangeNotification::Operation operation);
+ qint64 leftId;
+ qint64 rightId;
+ QString type;
+ };
- QByteArray sessionId() const;
- void setSessionId(const QByteArray &sessionId);
+ explicit ItemChangeNotification();
+ ItemChangeNotification(const Command &other);
+
+ bool isValid() const;
+
+ Operation operation() const;
+ void setOperation(Operation operation);
- void addEntity(Id id, const QString &remoteId = QString(), const QString &remoteRevision = QString(), const QString &mimeType = QString());
- void setEntities(const QVector<Entity> &items);
- QMap<Id, Entity> entities() const;
- Entity entity(Id id) const;
+ void addItem(Id id, const QString &remoteId = QString(), const QString &remoteRevision = QString(), const QString &mimeType = QString());
+ void setItem(const QVector<Item> &items);
+ QMap<Id, Item> items() const;
+ Item item(Id id) const;
QList<Id> uids() const;
- void clearEntities();
+ void clearItems();
QByteArray resource() const;
void setResource(const QByteArray &resource);
@@ -2120,95 +2158,397 @@ public:
QSet<qint64> removedTags() const;
void setRemovedTags(const QSet<qint64> &tags);
- void addMetadata(const QByteArray &metadata);
- void removeMetadata(const QByteArray &metadata);
- QVector<QByteArray> metadata() const;
+ QSet<Relation> addedRelations() const;
+ void setAddedRelations(const QSet<Relation> &relations);
+
+ QSet<Relation> removedRelations() const;
+ void setRemovedRelations(const QSet<Relation> &relations);
+private:
+ AKONADI_DECLARE_PRIVATE(ItemChangeNotification)
+
+ friend DataStream &operator<<(DataStream &stream, const Akonadi::Protocol::ItemChangeNotification &command);
+ friend DataStream &operator>>(DataStream &stream, Akonadi::Protocol::ItemChangeNotification &command);
+};
+
+inline uint qHash(const ItemChangeNotification::Relation &rel)
+{
+ return ::qHash(rel.leftId + rel.rightId);
+}
+
+
+
+class CollectionChangeNotificationPrivate;
+class AKONADIPRIVATE_EXPORT CollectionChangeNotification : public ChangeNotification
+{
+public:
+ typedef QVector<CollectionChangeNotification> List;
+
+ enum Operation : uchar {
+ InvalidOp,
+ Add,
+ Modify,
+ Move,
+ Remove,
+ Subscribe,
+ Unsubscribe
+ };
+
+ explicit CollectionChangeNotification();
+ CollectionChangeNotification(const Command& other);
+
+ bool isValid() const;
+
+ Operation operation() const;
+ void setOperation(Operation operation);
+
+ Id id() const;
+ void setId(Id id);
+
+ QString remoteId() const;
+ void setRemoteId(const QString &remoteId);
+
+ QString remoteRevision() const;
+ void setRemoteRevision(const QString &remoteRevision);
+
+ QByteArray resource() const;
+ void setResource(const QByteArray &resource);
+
+ Id parentCollection() const;
+ void setParentCollection(Id parent);
+
+ Id parentDestCollection() const;
+ void setParentDestCollection(Id parent);
+
+ QByteArray destinationResource() const;
+ void setDestinationResource(const QByteArray &destResource);
+
+ QSet<QByteArray> changedParts() const;
+ void setChangedParts(const QSet<QByteArray> &parts);
/**
Adds a new notification message to the given list and compresses notifications
where possible.
*/
- static bool appendAndCompress(ChangeNotification::List &list, const ChangeNotification &msg);
+ static bool appendAndCompress(ChangeNotification::List &list, const CollectionChangeNotification &msg);
private:
- AKONADI_DECLARE_PRIVATE(ChangeNotification)
+ AKONADI_DECLARE_PRIVATE(CollectionChangeNotification)
- friend DataStream &operator<<(DataStream &stream, const Akonadi::Protocol::ChangeNotification &command);
- friend DataStream &operator>>(DataStream &stream, Akonadi::Protocol::ChangeNotification &command);
+ friend DataStream &operator<<(DataStream &stream, const Akonadi::Protocol::CollectionChangeNotification &command);
+ friend DataStream &operator>>(DataStream &stream, Akonadi::Protocol::CollectionChangeNotification &command);
};
-inline uint qHash(ChangeNotification::Type type)
+
+
+class TagChangeNotificationPrivate;
+class AKONADIPRIVATE_EXPORT TagChangeNotification : public ChangeNotification
{
- return ::qHash(static_cast<uchar>(type));
-}
+public:
+ typedef QVector<TagChangeNotification> List;
+
+ enum Operation : uchar {
+ InvalidOp,
+ Add,
+ Modify,
+ Remove
+ };
+
+ explicit TagChangeNotification();
+ TagChangeNotification(const Command& other);
+
+ bool isValid() const;
+
+ Operation operation() const;
+ void setOperation(Operation operation);
-#if 0
-class ChangeNotificationSubscriptionCommandPrivate;
-class AKONADIPRIVATE_EXPORT ChangeNotificationSubscriptionCommand : public Command
+ void setId(Id id);
+ Id id() const;
+
+ void setRemoteId(const QString &remoteId);
+ QString remoteId() const;
+
+ QByteArray resource() const;
+ void setResource(const QByteArray &resource);
+
+private:
+ AKONADI_DECLARE_PRIVATE(TagChangeNotification)
+
+ friend DataStream &operator<<(DataStream &stream, const Akonadi::Protocol::TagChangeNotification &command);
+ friend DataStream &operator>>(DataStream &stream, Akonadi::Protocol::TagChangeNotification &command);
+};
+
+
+
+class RelationChangeNotificationPrivate;
+class AKONADIPRIVATE_EXPORT RelationChangeNotification : public ChangeNotification
{
public:
- explicit ChangeNotificationSubscriptionCommand();
- explicit ChangeNotificationSubscriptionCommand(const QByteArray &subscriptionId);
- ChangeNotificationSubscriptionCommand(const Command &other);
+ typedef QVector<RelationChangeNotification> List;
+
+ enum Operation : uchar {
+ InvalidOp,
+ Add,
+ Remove
+ };
+
+ explicit RelationChangeNotification();
+ RelationChangeNotification(const Command &other);
- void unsubscribe();
+ Operation operation() const;
+ void setOperation(Operation operation);
- void monitorCollection(qint64 id, bool monitor = true);
- void setMonitoredCollections(const QVector<qint64> &ids);
- QVector<qint64> monitoredCollections() const;
+ Id leftItem() const;
+ void setLeftItem(Id left);
- void monitorItem(qint64 id, bool monitor = true);
- void setMonitoredItems(const QVector<qint64> &ids);
- QVector<qint64> monitoredItems() const;
+ Id rightItem() const;
+ void setRightItem(Id right);
- void monitorTag(qint64 id, bool monitor = true);
- void setMonitoredTags(const QVector<qint64> &ids);
- QVector<qint64> monitoredTags() const;
+ QString remoteId() const;
+ void setRemoteId(const QString &remoteId);
- void monitorType(ChangeNotification::Type type, bool monitor = true);
- void setMonitoredTypes(const QVector<ChangeNotification::Type> &types);
- QVector<ChangeNotification::Type> monitoredTypes() const;
+ QString type() const;
+ void setType(const QString &type);
- void monitorResource(const QByteArray &resourceId, bool monitor = true);
- void setMonitoredResources(const QVector<QByteArray> &resources);
- QVector<QByteArray> monitoredResources() const;
+private:
+ AKONADI_DECLARE_PRIVATE(RelationChangeNotification)
- void monitorMimeType(const QString &mimeType, bool monitor = true);
- void setMonitoredMimeTypes(const QStringList &mimeTypes);
- QStringList monitoredMimeTypes() const;
+ friend DataStream &operator<<(DataStream &stream, const Akonadi::Protocol::RelationChangeNotification &command);
+ friend DataStream &operator>>(DataStream &stream, Akonadi::Protocol::RelationChangeNotification &command);
+};
- void setAllMonitored(bool all);
- bool isAllMonitored() const;
+
+
+
+class CreateSubscriptionCommandPrivate;
+class AKONADIPRIVATE_EXPORT CreateSubscriptionCommand : public Command
+{
+public:
+ explicit CreateSubscriptionCommand();
+ explicit CreateSubscriptionCommand(const QByteArray &subscriberName,
+ const QByteArray &session);
+ CreateSubscriptionCommand(const Command &other);
+
+ void setSubscriberName(const QByteArray &name);
+ QByteArray subscriberName() const;
+
+ void setSession(const QByteArray &session);
+ QByteArray session() const;
+
+private:
+ AKONADI_DECLARE_PRIVATE(CreateSubscriptionCommand)
+
+ friend DataStream &operator<<(DataStream &stream, const Akonadi::Protocol::CreateSubscriptionCommand &command);
+ friend DataStream &operator>>(DataStream &stream, Akonadi::Protocol::CreateSubscriptionCommand &command);
+};
+
+
+class AKONADIPRIVATE_EXPORT CreateSubscriptionResponse : public Response
+{
+public:
+ explicit CreateSubscriptionResponse();
+ CreateSubscriptionResponse(const Command &other);
+};
+
+
+
+
+class ModifySubscriptionCommandPrivate;
+class AKONADIPRIVATE_EXPORT ModifySubscriptionCommand : public Command
+{
+public:
+ enum ModifiedPart {
+ None = 0,
+ Collections = 1 << 0,
+ Items = 1 << 1,
+ Tags = 1 << 2,
+ Types = 1 << 3,
+ Resources = 1 << 4,
+ MimeTypes = 1 << 5,
+ AllFlag = 1 << 6,
+ ExclusiveFlag = 1 << 7,
+ Sessions = 1 << 8,
+
+ Add = 1 << 14,
+ Remove = 1 << 15
+ };
+ Q_DECLARE_FLAGS(ModifiedParts, ModifiedPart)
+
+ enum ChangeType {
+ NoType = 0,
+ ItemChanges,
+ CollectionChanges,
+ TagChanges,
+ RelationChanges,
+ SubscriptionChanges
+ };
+
+ explicit ModifySubscriptionCommand();
+ ModifySubscriptionCommand(const Command &other);
+
+ void setSubscriberName(const QByteArray &subscriberName);
+ QByteArray subscriberName() const;
+
+ ModifiedParts modifiedParts() const;
+
+ void startMonitoringCollection(qint64 id);
+ QVector<qint64> startMonitoringCollections() const;
+
+ void stopMonitoringCollection(qint64 id);
+ QVector<qint64> stopMonitoringCollections() const;
+
+ void startMonitoringItem(qint64 id);
+ QVector<qint64> startMonitoringItems() const;
+
+ void stopMonitoringItem(qint64 id);
+ QVector<qint64> stopMonitoringItems() const;
+
+ void startMonitoringTag(qint64 id);
+ QVector<qint64> startMonitoringTags() const;
+
+ void stopMonitoringTag(qint64 id);
+ QVector<qint64> stopMonitoringTags() const;
+
+ void startMonitoringType(ChangeType type);
+ QVector<ChangeType> startMonitoringTypes() const;
+
+ void stopMonitoringType(ChangeType type);
+ QVector<ChangeType> stopMonitoringTypes() const;
+
+ void startMonitoringResource(const QByteArray &resource);
+ QVector<QByteArray> startMonitoringResources() const;
+
+ void stopMonitoringResource(const QByteArray &resource);
+ QVector<QByteArray> stopMonitoringResources() const;
+
+ void startMonitoringMimeType(const QString &mimeType);
+ QStringList startMonitoringMimeTypes() const;
+
+ void stopMonitoringMimeType(const QString &mimeType);
+ QStringList stopMonitoringMimeTypes() const;
+
+ void setAllMonitored(bool allMonitored);
+ bool allMonitored() const;
void setExclusive(bool exclusive);
bool isExclusive() const;
- void ignoreSession(const QByteArray &sessionId, bool ignored = true);
- void setIgnoredSessions(const QVector<QByteArray> &ignoredSessions);
- QVector<QByteArray> ignoredSessions() const;
+ void startIgnoringSession(const QByteArray &sessionId);
+ QVector<QByteArray> startIgnoringSessions() const;
+
+ void stopIgnoringSession(const QByteArray &sessionId);
+ QVector<QByteArray> stopIgnoringSessions() const;
private:
- AKONADI_DECLARE_PRIVATE(ChangeNotificationSubscriptionCommand)
+ AKONADI_DECLARE_PRIVATE(ModifySubscriptionCommand)
+
+ friend DataStream &operator<<(DataStream &stream, const Akonadi::Protocol::ModifySubscriptionCommand &command);
+ friend DataStream &operator>>(DataStream &stream, Akonadi::Protocol::ModifySubscriptionCommand &command);
+};
+
+
+class ModifySubscriptionResponse;
+class AKONADIPRIVATE_EXPORT ModifySubscriptionResponse : public Response
+{
+public:
+ explicit ModifySubscriptionResponse();
+ ModifySubscriptionResponse(const Command &other);
- friend DataStream &operator<<(DataStream &stream, const Akonadi::Protocol::ChangeNotificationSubscriptionCommand &command);
- friend DataStream &operator>>(DataStream &stream, Akonadi::Protocol::ChangeNotificationSubscriptionCommand &command);
+private:
+ friend DataStream &operator<<(DataStream &stream, const Akonadi::Protocol::ModifySubscriptionResponse &command);
+ friend DataStream &operator>>(DataStream &stream, Akonadi::Protocol::ModifySubscriptionResponse &command);
};
-class ChangeNotificationSubscriptionResponsePrivate;
-class AKONADIPRIVATE_EXPORT ChangeNotificationSubscriptionResponse : public Response
+
+class SubscriptionChangeNotificationPrivate;
+class AKONADIPRIVATE_EXPORT SubscriptionChangeNotification : public ChangeNotification
{
public:
- explicit ChangeNotificationSubscriptionResponse();
- explicit ChangeNotificationSubscriptionResponse(const QByteArray &subscriptionId);
- ChangeNotificationSubscriptionResponse(const Command &other);
+ enum Operation {
+ InvalidOp,
+ Add,
+ Modify,
+ Remove
+ };
+
+ enum ModifiedPart {
+ None = 0,
+ Collections = 1 << 0,
+ Items = 1 << 1,
+ Tags = 1 << 2,
+ Types = 1 << 3,
+ MimeTypes = 1 << 4,
+ Resources = 1 << 5,
+ IgnoredSessions = 1 << 6,
+ Monitored = 1 << 7,
+ Exclusive = 1 << 8
+ };
+ Q_DECLARE_FLAGS(ModifiedParts, ModifiedPart)
+
+ explicit SubscriptionChangeNotification();
+ SubscriptionChangeNotification(const Command &other);
+
+ Operation operation() const;
+ void setOperation(Operation operation);
+
+ ModifiedParts modifiedParts() const;
+
+ QByteArray subscriber() const;
+ void setSubscriber(const QByteArray &subscriber);
+
+ QSet<Id> addedCollections() const;
+ void setAddedCollections(const QSet<Id> &addedCols);
+ QSet<Id> removedCollections() const;
+ void setRemovedCollections(const QSet<Id> &removedCols);
+
+ QSet<Id> addedItems() const;
+ void setAddedItems(const QSet<Id> &addedItems);
+ QSet<Id> removedItems() const;
+ void setRemovedItems(const QSet<Id> &removedItems);
+
+ QSet<Id> addedTags() const;
+ void setAddedTags(const QSet<Id> &addedTags);
+ QSet<Id> removedTags() const;
+ void setRemovedTags(const QSet<Id> &removedTags);
+
+ QSet<ModifySubscriptionCommand::ChangeType> addedTypes() const;
+ void setAddedTypes(const QSet<ModifySubscriptionCommand::ChangeType> &addedTypes);
+ QSet<ModifySubscriptionCommand::ChangeType> removedTypes() const;
+ void setRemovedTypes(const QSet<ModifySubscriptionCommand::ChangeType> &removedTypes);
+
+ QSet<QString> addedMimeTypes() const;
+ void setAddedMimeTypes(const QSet<QString> &addedMimeTypes);
+ QSet<QString> removedMimeTypes() const;
+ void setRemovedMimeTypes(const QSet<QString> &removedMimeTypes);
+
+ QSet<QByteArray> addedResources() const;
+ void setAddedResources(const QSet<QByteArray> &addedResources);
+ QSet<QByteArray> removedResources() const;
+ void setRemovedResources(const QSet<QByteArray> &removedResouces);
+
+ QSet<QByteArray> addedIgnoredSessions() const;
+ void setAddedIgnoredSessions(const QSet<QByteArray> &ignoredSessions);
+ QSet<QByteArray> removedIgnoredSessions() const;
+ void setRemovedIgnoredSessions(const QSet<QByteArray> &ignoredSessions);
+
+ bool isAllMonitored() const;
+ void setAllMonitored(bool allMonitored);
+
+ bool isExclusive() const;
+ void setExclusive(bool exclusive);
private:
- friend DataStream &operator<<(DataStream &stream, const Akonadi::Protocol::ChangeNotificationSubscriptionResponse &command);
- friend DataStream &operator>>(DataStream &stream, Akonadi::Protocol::ChangeNotificationSubscriptionResponse &command);
+ AKONADI_DECLARE_PRIVATE(SubscriptionChangeNotification)
+
+ friend DataStream &operator<<(DataStream &stream, const Akonadi::Protocol::SubscriptionChangeNotification &ntf);
+ friend DataStream &operator>>(DataStream &stream, Akonadi::Protocol::SubscriptionChangeNotification &ntf);
};
-#endif
+
+
+
+
+
} // namespace Protocol
} // namespace Akonadi
@@ -2217,28 +2557,12 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(Akonadi::Protocol::FetchScope::FetchFlags)
Q_DECLARE_METATYPE(Akonadi::Protocol::Command::Type)
Q_DECLARE_METATYPE(Akonadi::Protocol::Command)
Q_DECLARE_METATYPE(Akonadi::Protocol::ChangeNotification)
-Q_DECLARE_METATYPE(Akonadi::Protocol::ChangeNotification::Type)
Q_DECLARE_METATYPE(Akonadi::Protocol::ChangeNotification::List)
AKONADIPRIVATE_EXPORT DataStream &operator>>(DataStream &stream, Akonadi::Protocol::Command::Type &type);
AKONADIPRIVATE_EXPORT DataStream &operator<<(DataStream &stream, Akonadi::Protocol::Command::Type type);
AKONADIPRIVATE_EXPORT QDebug operator<<(QDebug dbg, Akonadi::Protocol::Command::Type type);
-AKONADIPRIVATE_EXPORT inline const QDBusArgument &operator>>(const QDBusArgument &arg, Akonadi::Protocol::ChangeNotification::Type &type)
-{
- arg.beginStructure();
- arg >> reinterpret_cast<int&>(type);
- arg.endStructure();
- return arg;
-}
-
-AKONADIPRIVATE_EXPORT inline QDBusArgument &operator<<(QDBusArgument &arg, Akonadi::Protocol::ChangeNotification::Type type)
-{
- arg.beginStructure();
- arg << (int) type;
- arg.endStructure();
- return arg;
-}
// Command parameters
#define AKONADI_PARAM_ATR "ATR:"
diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt
index 3d5841b..4548655 100644
--- a/src/server/CMakeLists.txt
+++ b/src/server/CMakeLists.txt
@@ -39,6 +39,7 @@ akonadi_generate_schema(${AKONADI_DB_SCHEME} AkonadiSchema akonadischema)
set(libakonadiserver_SRCS
akonadi.cpp
+ aklocalserver.cpp
akthread.cpp
commandcontext.cpp
connection.cpp
@@ -124,7 +125,7 @@ set(libakonadiserver_SRCS
dbustracer.cpp
filetracer.cpp
notificationmanager.cpp
- notificationsource.cpp
+ notificationsubscriber.cpp
resourcemanager.cpp
cachecleaner.cpp
debuginterface.cpp
@@ -141,9 +142,7 @@ qt5_generate_dbus_interface(debuginterface.h org.freedesktop.Akonadi.DebugInterf
qt5_add_dbus_adaptor(libakonadiserver_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.TracerNotification.xml dbustracer.h Akonadi::Server::DBusTracer)
qt5_add_dbus_adaptor(libakonadiserver_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.Tracer.xml tracer.h Akonadi::Server::Tracer)
-qt5_add_dbus_adaptor(libakonadiserver_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.NotificationManager.xml notificationmanager.h Akonadi::Server::NotificationManager)
qt5_add_dbus_adaptor(libakonadiserver_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.Server.xml akonadi.h Akonadi::Server::AkonadiServer)
-qt5_add_dbus_adaptor(libakonadiserver_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.NotificationSource.xml notificationsource.h Akonadi::Server::NotificationSource)
qt5_add_dbus_adaptor(libakonadiserver_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.StorageDebugger.xml storage/storagedebugger.h Akonadi::Server::StorageDebugger)
qt5_add_dbus_adaptor(libakonadiserver_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.Akonadi.DebugInterface.xml debuginterface.h Akonadi::Server::DebugInterface)
qt5_add_dbus_adaptor(libakonadiserver_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.ResourceManager.xml resourcemanager.h Akonadi::Server::ResourceManager)
@@ -157,6 +156,7 @@ qt5_add_dbus_interface(libakonadiserver_SRCS ${Akonadi_SOURCE_DIR}/src/interface
qt5_add_resources(libakonadiserver_SRCS storage/akonadidb.qrc)
add_library(libakonadiserver STATIC ${libakonadiserver_SRCS})
+set_target_properties(libakonadiserver PROPERTIES OUTPUT_NAME akonadiserver)
target_link_libraries(libakonadiserver
akonadi_shared
KF5AkonadiPrivate
diff --git a/src/server/aklocalserver.cpp b/src/server/aklocalserver.cpp
new file mode 100644
index 0000000..6d2824b
--- /dev/null
+++ b/src/server/aklocalserver.cpp
@@ -0,0 +1,33 @@
+/*
+ Copyright (c) 2016 Daniel Vrátil <dvratil@kde.org>
+
+ 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 "aklocalserver.h"
+
+using namespace Akonadi::Server;
+
+AkLocalServer::AkLocalServer(QObject* parent)
+ : QLocalServer(parent)
+{
+}
+
+void AkLocalServer::incomingConnection(quintptr socketDescriptor)
+{
+ Q_EMIT newConnection(socketDescriptor);
+}
+
diff --git a/src/core/notificationbus_p.h b/src/server/aklocalserver.h
index 9c568ca..37ba4a1 100644
--- a/src/core/notificationbus_p.h
+++ b/src/server/aklocalserver.h
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2015 Daniel Vrátil <dvratil@redhat.com>
+ Copyright (c) 2016 Daniel Vrátil <dvratil@kde.org>
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
@@ -17,33 +17,29 @@
02110-1301, USA.
*/
-#ifndef AKONADI_NOTIFICATIONBUS_P_H
-#define AKONADI_NOTIFICATIONBUS_P_H
+#ifndef AKLOCALSERVER_H
+#define AKLOCALSERVER_H
-#include "session_p.h"
+#include <QLocalServer>
-namespace Akonadi
-{
-
-namespace Protocol
-{
-class ChangeNotification;
-}
+namespace Akonadi {
+namespace Server {
-class NotificationBusPrivate : public QObject,
- public Akonadi::SessionPrivate
+class AkLocalServer : public QLocalServer
{
Q_OBJECT
-
public:
- explicit NotificationBusPrivate(Session *parent = Q_NULLPTR);
- ~NotificationBusPrivate();
-
- bool handleCommand(qint64 tag, const Protocol::Command &cmd) Q_DECL_OVERRIDE;
+ AkLocalServer(QObject *parent = Q_NULLPTR);
Q_SIGNALS:
- void notify(const Akonadi::Protocol::ChangeNotification &ntf);
+ void newConnection(quintptr socketDescriptor);
+
+protected:
+ void incomingConnection(quintptr socketDescriptor) Q_DECL_OVERRIDE;
};
-}
-#endif // AKONADI_NOTIFICATIONBUS_P_H
+
+} // namespace Server
+} // namespace Akonadi
+
+#endif
diff --git a/src/server/akonadi.cpp b/src/server/akonadi.cpp
index f660b59..2dd11f4 100644
--- a/src/server/akonadi.cpp
+++ b/src/server/akonadi.cpp
@@ -37,6 +37,7 @@
#include "preprocessormanager.h"
#include "search/searchmanager.h"
#include "search/searchtaskmanager.h"
+#include "aklocalserver.h"
#include "collectionreferencemanager.h"
@@ -44,6 +45,7 @@
#include <private/standarddirs_p.h>
#include <private/protocol_p.h>
#include <private/dbus_p.h>
+#include <private/instance_p.h>
#include <QtSql/QSqlQuery>
#include <QtSql/QSqlError>
@@ -53,16 +55,7 @@
#include <QtCore/QSettings>
#include <QtCore/QTimer>
#include <QtDBus/QDBusServiceWatcher>
-
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-#include <stdlib.h>
-
-#ifdef Q_OS_WIN
-#include <windows.h>
-#include <Sddl.h>
-#endif
+#include <QtNetwork/QLocalServer>
using namespace Akonadi;
using namespace Akonadi::Server;
@@ -70,7 +63,10 @@ using namespace Akonadi::Server;
AkonadiServer *AkonadiServer::s_instance = 0;
AkonadiServer::AkonadiServer(QObject *parent)
- : QLocalServer(parent)
+ : QObject(parent)
+ , mCmdServer(Q_NULLPTR)
+ , mNtfServer(Q_NULLPTR)
+ , mNotificationManager(Q_NULLPTR)
, mCacheCleaner(Q_NULLPTR)
, mIntervalCheck(Q_NULLPTR)
, mStorageJanitor(Q_NULLPTR)
@@ -84,9 +80,7 @@ AkonadiServer::AkonadiServer(QObject *parent)
qRegisterMetaType<Protocol::Command>();
qRegisterMetaType<Protocol::ChangeNotification>();
qRegisterMetaType<Protocol::ChangeNotification::List>();
-
- qRegisterMetaType<Protocol::ChangeNotification::Type>();
- qDBusRegisterMetaType<Protocol::ChangeNotification::Type>();
+ qRegisterMetaType<quintptr>("quintptr");
}
bool AkonadiServer::init()
@@ -120,6 +114,17 @@ bool AkonadiServer::init()
const QString connectionSettingsFile = StandardDirs::connectionConfigFile(XdgBaseDirs::WriteOnly);
QSettings connectionSettings(connectionSettingsFile, QSettings::IniFormat);
+ mCmdServer = new AkLocalServer(this);
+ connect(mCmdServer, static_cast<void(AkLocalServer::*)(quintptr)>(&AkLocalServer::newConnection),
+ this, &AkonadiServer::newCmdConnection);
+
+ mNotificationManager = new NotificationManager();
+ mNtfServer = new AkLocalServer(this);
+ // Note: this is a queued connection, as NotificationManager lives in its
+ // own thread
+ connect(mNtfServer, static_cast<void(AkLocalServer::*)(quintptr)>(&AkLocalServer::newConnection),
+ mNotificationManager, &NotificationManager::registerConnection);
+
#ifdef Q_OS_WIN
HANDLE hToken = NULL;
PSID sid;
@@ -152,29 +157,49 @@ bool AkonadiServer::init()
}
free(sid);
}
- QString defaultPipe = QLatin1String("Akonadi-") + userID;
- QString namedPipe = settings.value(QLatin1String("Connection/NamedPipe"), defaultPipe).toString();
- if (!listen(namedPipe)) {
- qCCritical(AKONADISERVER_LOG) << "Unable to listen on Named Pipe" << namedPipe;
+ const QString defaultCmdPipe = QStringLiteral("Akonadi-Cmd-") % userID;
+ const QString cmdPipe = settings.value(QStringLiteral("Connection/NamedPipe"), defaultCmdPipe).toString();
+ if (!mCmdServer->listen(cmdPipe)) {
+ qCCritical(AKONADISERVER_LOG) << "Unable to listen on Named Pipe" << cmdPipe;
quit();
return false;
}
- connectionSettings.setValue(QLatin1String("Data/Method"), QLatin1String("NamedPipe"));
- connectionSettings.setValue(QLatin1String("Data/NamedPipe"), namedPipe);
+ const QString defaultNtfPipe = QStringLiteral("Akonadi-Ntf-") % userID;
+ const QString ntfPipe = settings.value(QStringLiteral("Connection/NtfNamedPipe"), defaultNtfPipe).toString();
+ if (!mNtfServer->listen(ntfPipe)) {
+ qCCritical(AKONADISERVER_LOG) << "Unable to listen on Named Pipe" << ntfPipe;
+ quit();
+ return false;
+ }
+
+ connectionSettings.setValue(QStringLiteral("Data/Method"), QStringLiteral("NamedPipe"));
+ connectionSettings.setValue(QStringLiteral("Data/NamedPipe"), cmdPipe);
+ connectionSettings.setValue(QStringLiteral("Notifications/Method"), QStringLiteral("NamedPipe"));
+ connectionSettings.setValue(QStringLiteral("Notifications/NamedPipe"), ntfPipe);
#else
const QString socketDir = Utils::preferredSocketDirectory(StandardDirs::saveDir("data"));
- const QString socketFile = socketDir + QLatin1String("/akonadiserver.socket");
- QFile::remove(socketFile);
- if (!listen(socketFile)) {
- qCCritical(AKONADISERVER_LOG) << "Unable to listen on Unix socket" << socketFile;
+ const QString cmdSocketFile = socketDir % QStringLiteral("/akonadiserver-cmd.socket");
+ QFile::remove(cmdSocketFile);
+ if (!mCmdServer->listen(cmdSocketFile)) {
+ qCCritical(AKONADISERVER_LOG) << "Unable to listen on Unix socket" << cmdSocketFile;
+ quit();
+ return false;
+ }
+
+ const QString ntfSocketFile = socketDir % QStringLiteral("/akonadiserver-ntf.socket");
+ QFile::remove(ntfSocketFile);
+ if (!mNtfServer->listen(ntfSocketFile)) {
+ qCCritical(AKONADISERVER_LOG) << "Unable to listen on Unix socket" << ntfSocketFile;
quit();
return false;
}
- connectionSettings.setValue(QStringLiteral("Data/Method"), QLatin1String("UnixPath"));
- connectionSettings.setValue(QStringLiteral("Data/UnixPath"), socketFile);
+ connectionSettings.setValue(QStringLiteral("Data/Method"), QStringLiteral("UnixPath"));
+ connectionSettings.setValue(QStringLiteral("Data/UnixPath"), cmdSocketFile);
+ connectionSettings.setValue(QStringLiteral("Notifications/Method"), QStringLiteral("UnixPath"));
+ connectionSettings.setValue(QStringLiteral("Notifications/UnixPath"), ntfSocketFile);
#endif
// initialize the database
@@ -190,7 +215,6 @@ bool AkonadiServer::init()
return false;
}
- NotificationManager::self();
Tracer::self();
new DebugInterface(this);
ResourceManager::self();
@@ -276,6 +300,10 @@ bool AkonadiServer::quit()
}
mAlreadyShutdown = true;
+ qCDebug(AKONADISERVER_LOG) << "terminating connection threads";
+ qDeleteAll(mConnections);
+ mConnections.clear();
+
qCDebug(AKONADISERVER_LOG) << "terminating service threads";
delete mCacheCleaner;
delete mIntervalCheck;
@@ -283,12 +311,7 @@ bool AkonadiServer::quit()
delete mItemRetrieval;
delete mAgentSearchManager;
delete mSearchManager;
-
- qCDebug(AKONADISERVER_LOG) << "terminating connection threads";
- for (int i = 0; i < mConnections.count(); ++i) {
- delete mConnections[i];
- }
- mConnections.clear();
+ delete mNotificationManager;
// Terminate the preprocessor manager before the database but after all connections are gone
PreprocessorManager::done();
@@ -304,13 +327,6 @@ bool AkonadiServer::quit()
QSettings settings(StandardDirs::serverConfigFile(), QSettings::IniFormat);
const QString connectionSettingsFile = StandardDirs::connectionConfigFile(XdgBaseDirs::WriteOnly);
-#ifndef Q_OS_WIN
- const QString socketDir = Utils::preferredSocketDirectory(StandardDirs::saveDir("data"));
-
- if (!QDir::home().remove(socketDir + QLatin1String("/akonadiserver.socket"))) {
- qCCritical(AKONADISERVER_LOG) << "Failed to remove Unix socket";
- }
-#endif
if (!QDir::home().remove(connectionSettingsFile)) {
qCCritical(AKONADISERVER_LOG) << "Failed to remove runtime connection config file";
}
@@ -325,15 +341,17 @@ void AkonadiServer::doQuit()
QCoreApplication::exit();
}
-void AkonadiServer::incomingConnection(quintptr socketDescriptor)
+void AkonadiServer::newCmdConnection(quintptr socketDescriptor)
{
if (mAlreadyShutdown) {
return;
}
- QPointer<Connection> connection = new Connection(socketDescriptor);
- connect(connection.data(), &Connection::disconnected,
- this, [connection]() {
- delete connection.data();
+
+ Connection *connection = new Connection(socketDescriptor);
+ connect(connection, &Connection::disconnected,
+ this, [this, connection]() {
+ delete connection;
+ mConnections.removeOne(connection);
}, Qt::QueuedConnection);
mConnections.append(connection);
}
@@ -422,23 +440,22 @@ void AkonadiServer::serviceOwnerChanged(const QString &name, const QString &oldO
CacheCleaner *AkonadiServer::cacheCleaner()
{
- if (mCacheCleaner) {
- return mCacheCleaner;
- }
-
- return Q_NULLPTR;
+ return mCacheCleaner;
}
IntervalCheck *AkonadiServer::intervalChecker()
{
- if (mIntervalCheck) {
- return mIntervalCheck;
- }
+ return mIntervalCheck;
+}
- return Q_NULLPTR;
+NotificationManager *AkonadiServer::notificationManager()
+{
+ return mNotificationManager;
}
QString AkonadiServer::serverPath() const
{
return XdgBaseDirs::homePath("config");
}
+
+#include "akonadi.moc"
diff --git a/src/server/akonadi.h b/src/server/akonadi.h
index 5ea7112..f65d6b7 100644
--- a/src/server/akonadi.h
+++ b/src/server/akonadi.h
@@ -20,11 +20,10 @@
#ifndef AKONADISERVER_H
#define AKONADISERVER_H
+#include <QtCore/QObject>
#include <QtCore/QPointer>
#include <QtCore/QVector>
-#include <QtNetwork/QLocalServer>
-
class QProcess;
namespace Akonadi {
@@ -38,8 +37,10 @@ class SearchManager;
class StorageJanitor;
class CacheCleaner;
class IntervalCheck;
+class AkLocalServer;
+class NotificationManager;
-class AkonadiServer : public QLocalServer
+class AkonadiServer : public QObject
{
Q_OBJECT
@@ -59,6 +60,11 @@ public:
QString serverPath() const;
+ /**
+ * Can return a nullptr
+ */
+ NotificationManager *notificationManager();
+
public Q_SLOTS:
/**
* Triggers a clean server shutdown.
@@ -67,29 +73,34 @@ public Q_SLOTS:
virtual bool init();
+protected Q_SLOTS:
+ virtual void newCmdConnection(quintptr socketDescriptor);
+
private Q_SLOTS:
void doQuit();
void serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner);
-protected:
- /** reimpl */
- void incomingConnection(quintptr socketDescriptor) Q_DECL_OVERRIDE;
-
private:
bool startDatabaseProcess();
bool createDatabase();
void stopDatabaseProcess();
+ uint userId() const;
+
protected:
AkonadiServer(QObject *parent = Q_NULLPTR);
+ AkLocalServer *mCmdServer;
+ AkLocalServer *mNtfServer;
+
+ NotificationManager *mNotificationManager;
CacheCleaner *mCacheCleaner;
IntervalCheck *mIntervalCheck;
StorageJanitor *mStorageJanitor;
ItemRetrievalManager *mItemRetrieval;
SearchTaskManager *mAgentSearchManager;
QProcess *mDatabaseProcess;
- QVector<QPointer<Connection>> mConnections;
+ QVector<Connection *> mConnections;
SearchManager *mSearchManager;
bool mAlreadyShutdown;
diff --git a/src/server/connection.cpp b/src/server/connection.cpp
index 8eee50c..5a16a35 100644
--- a/src/server/connection.cpp
+++ b/src/server/connection.cpp
@@ -51,7 +51,6 @@ Connection::Connection(QObject *parent)
, m_socket(0)
, m_currentHandler(0)
, m_connectionState(NonAuthenticated)
- , m_isNotificationBus(false)
, m_backend(0)
, m_verifyCacheOnRetrieval(false)
, m_idleTimer(Q_NULLPTR)
@@ -112,7 +111,6 @@ void Connection::quit()
{
Tracer::self()->endConnection(m_identifier, QString());
collectionReferenceManager()->removeSession(m_sessionId);
- NotificationManager::self()->unregisterConnection(this);
delete m_socket;
m_socket = 0;
@@ -170,11 +168,6 @@ void Connection::slotConnectionIdle()
void Connection::slotNewData()
{
- if (m_isNotificationBus) {
- qWarning() << "Connection" << sessionId() << ": received data when in NotificationBus mode!";
- return;
- }
-
m_idleTimer->stop();
// will only open() a previously idle backend.
@@ -326,28 +319,6 @@ QByteArray Connection::sessionId() const
return m_sessionId;
}
-void Connection::setIsNotificationBus(bool on)
-{
- if (m_isNotificationBus == on) {
- return;
- }
-
- m_isNotificationBus = on;
- if (m_isNotificationBus) {
- qCDebug(AKONADISERVER_LOG()) << "New notification bus:" << m_sessionId;
- NotificationManager::self()->registerConnection(this);
- } else {
- NotificationManager::self()->unregisterConnection(this);
- }
-}
-
-bool Connection::isNotificationBus() const
-{
- return m_isNotificationBus;
-}
-
-
-
bool Connection::isOwnerResource(const PimItem &item) const
{
if (context()->resource().isValid() && item.collection().resourceId() == context()->resource().id()) {
@@ -402,11 +373,8 @@ void Connection::reportTime() const
void Connection::sendResponse(qint64 tag, const Protocol::Command &response)
{
- // Notifications have their own debugging system
- if (!m_isNotificationBus) {
- if (Tracer::self()->currentTracer() != QLatin1String("null")) {
- Tracer::self()->connectionOutput(m_identifier, QByteArray::number(tag) + ' ' + response.debugString().toUtf8());
- }
+ if (Tracer::self()->currentTracer() != QLatin1String("null")) {
+ Tracer::self()->connectionOutput(m_identifier, QByteArray::number(tag) + ' ' + response.debugString().toUtf8());
}
QDataStream stream(m_socket);
stream << tag;
@@ -415,13 +383,8 @@ void Connection::sendResponse(qint64 tag, const Protocol::Command &response)
void Connection::sendResponse(const Protocol::Command &response)
{
- if (m_isNotificationBus) {
- // FIXME: Don't hardcode the tag for notifications
- sendResponse(4, response);
- } else {
- Q_ASSERT(m_currentHandler);
- sendResponse(m_currentHandler->tag(), response);
- }
+ Q_ASSERT(m_currentHandler);
+ sendResponse(m_currentHandler->tag(), response);
}
Protocol::Command Connection::readCommand()
diff --git a/src/server/connection.h b/src/server/connection.h
index 810d277..3e4a874 100644
--- a/src/server/connection.h
+++ b/src/server/connection.h
@@ -67,13 +67,9 @@ public:
void setSessionId(const QByteArray &id);
QByteArray sessionId() const;
- void setIsNotificationBus(bool on);
- bool isNotificationBus() const;
-
/** Returns @c true if permanent cache verification is enabled. */
bool verifyCacheOnRetrieval() const;
-
Protocol::Command readCommand();
public Q_SLOTS:
@@ -105,7 +101,6 @@ protected:
QLocalSocket *m_socket;
QPointer<Handler> m_currentHandler;
ConnectionState m_connectionState;
- bool m_isNotificationBus;
mutable DataStore *m_backend;
QList<QByteArray> m_statusMessageQueue;
QString m_identifier;
diff --git a/src/server/handler.cpp b/src/server/handler.cpp
index e0f0896..d23ae40 100644
--- a/src/server/handler.cpp
+++ b/src/server/handler.cpp
@@ -113,29 +113,24 @@ Handler *Handler::findHandlerForCommandAuthenticated(Protocol::Command::Type cmd
switch (cmd)
{
case Protocol::Command::Invalid:
- Q_ASSERT_X(cmd != Protocol::Command::Invalid,
- "Handler::findHandlerForCommandAuthenticated()",
+ Q_ASSERT_X(cmd != Protocol::Command::Invalid, __FUNCTION__,
"Invalid command is not allowed");
return Q_NULLPTR;
case Protocol::Command::Hello:
- Q_ASSERT_X(cmd != Protocol::Command::Hello,
- "Handler::findHandlerForCommandAuthenticated()",
- "Hello command is not allowed in this context");
+ Q_ASSERT_X(cmd != Protocol::Command::Hello, __FUNCTION__,
+ "Hello command is not allowed in this context");
return Q_NULLPTR;
case Protocol::Command::Login:
- Q_ASSERT_X(cmd != Protocol::Command::StreamPayload,
- "Handler::findHandlerForCommandAuthenticated()",
- "Login command is not allowed in this context");
+ Q_ASSERT_X(cmd != Protocol::Command::StreamPayload, __FUNCTION__,
+ "Login command is not allowed in this context");
return Q_NULLPTR;
case Protocol::Command::Logout:
- Q_ASSERT_X(cmd != Protocol::Command::StreamPayload,
- "Handler::findHandlerForCommandAuthenticated()",
- "Logout command is not allowed in this context");
+ Q_ASSERT_X(cmd != Protocol::Command::StreamPayload, __FUNCTION__,
+ "Logout command is not allowed in this context");
return Q_NULLPTR;
case Protocol::Command::_ResponseBit:
- Q_ASSERT_X(cmd != Protocol::Command::_ResponseBit,
- "Handler::findHandlerForCommandAuthenticated()",
- "ResponseBit is not a valid command type");
+ Q_ASSERT_X(cmd != Protocol::Command::_ResponseBit, __FUNCTION__,
+ "ResponseBit is not a valid command type");
return Q_NULLPTR;
case Protocol::Command::Transaction:
@@ -198,14 +193,34 @@ Handler *Handler::findHandlerForCommandAuthenticated(Protocol::Command::Type cmd
return new ResourceSelect();
case Protocol::Command::StreamPayload:
- Q_ASSERT_X(cmd != Protocol::Command::StreamPayload,
- "Handler::findHandlerForCommandAuthenticated()",
- "StreamPayload command is not allowed in this context");
+ Q_ASSERT_X(cmd != Protocol::Command::StreamPayload, __FUNCTION__,
+ "StreamPayload command is not allowed in this context");
+ return Q_NULLPTR;
+
+ case Protocol::Command::ItemChangeNotification:
+ Q_ASSERT_X(cmd != Protocol::Command::ItemChangeNotification, __FUNCTION__,
+ "ItemChangeNotification command is not allowed on this connection");
+ return Q_NULLPTR;
+ case Protocol::Command::CollectionChangeNotification:
+ Q_ASSERT_X(cmd != Protocol::Command::CollectionChangeNotification, __FUNCTION__,
+ "CollectionChangeNotification command is not allowed on this connection");
+ return Q_NULLPTR;
+ case Protocol::Command::TagChangeNotification:
+ Q_ASSERT_X(cmd != Protocol::Command::TagChangeNotification, __FUNCTION__,
+ "TagChangeNotification command is not allowed on this connection");
+ return Q_NULLPTR;
+ case Protocol::Command::RelationChangeNotification:
+ Q_ASSERT_X(cmd != Protocol::Command::RelationChangeNotification, __FUNCTION__,
+ "RelationChangeNotification command is not allowed on this connection");
+ return Q_NULLPTR;
+ case Protocol::Command::ModifySubscription:
+ Q_ASSERT_X(cmd != Protocol::Command::ModifySubscription, __FUNCTION__,
+ "ModifySubscription command is not allowed on this connection");
+ return Q_NULLPTR;
+ case Protocol::Command::CreateSubscription:
+ Q_ASSERT_X(cmd != Protocol::Command::CreateSubscription, __FUNCTION__,
+ "CreateSubscription command is not allowed on this connection");
return Q_NULLPTR;
- case Protocol::Command::ChangeNotification:
- Q_ASSERT_X(cmd != Protocol::Command::ChangeNotification,
- "Handler::findHandlerForCommandNonAuthenticated()",
- "ChangeNotification command is not allowed in this context");
}
return Q_NULLPTR;
diff --git a/src/server/handler/login.cpp b/src/server/handler/login.cpp
index 96b55f4..f7d07a0 100644
--- a/src/server/handler/login.cpp
+++ b/src/server/handler/login.cpp
@@ -33,10 +33,6 @@ bool Login::parseStream()
}
connection()->setSessionId(cmd.sessionId());
- if (cmd.sessionMode() == Protocol::LoginCommand::NotificationBus) {
- connection()->setIsNotificationBus(true);
- }
-
Q_EMIT connectionStateChange(Server::Authenticated);
return successResponse<Protocol::LoginResponse>();
diff --git a/src/server/notificationmanager.cpp b/src/server/notificationmanager.cpp
index 88a3421..f3c1010 100644
--- a/src/server/notificationmanager.cpp
+++ b/src/server/notificationmanager.cpp
@@ -19,219 +19,126 @@
*/
#include "notificationmanager.h"
-#include "notificationmanageradaptor.h"
-#include "notificationsource.h"
+#include "notificationsubscriber.h"
+#include "storage/notificationcollector.h"
#include "tracer.h"
-#include "storage/datastore.h"
-#include "connection.h"
#include "akonadiserver_debug.h"
#include <private/standarddirs_p.h>
#include <private/xdgbasedirs_p.h>
-#include <QDBusConnection>
+#include <QLocalSocket>
#include <QSettings>
+#include <QCoreApplication>
+#include <QThreadPool>
+#include <QPointer>
using namespace Akonadi;
using namespace Akonadi::Server;
-NotificationManager *NotificationManager::mSelf = 0;
-
-Q_DECLARE_METATYPE(QVector<qint64>)
-
NotificationManager::NotificationManager()
- : QObject(0)
- , mDebug(false)
+ : AkThread()
{
- qRegisterMetaType<QVector<QByteArray>>();
- qDBusRegisterMetaType<QVector<QByteArray>>();
- qRegisterMetaType<Protocol::ChangeNotification::Type>();
- qDBusRegisterMetaType<Protocol::ChangeNotification::Type>();
- qRegisterMetaType<QVector<qint64>>();
- qDBusRegisterMetaType<QVector<qint64>>();
-
- new NotificationManagerAdaptor(this);
- QDBusConnection::sessionBus().registerObject(QStringLiteral("/notifications"),
- this, QDBusConnection::ExportAdaptors);
- QDBusConnection::sessionBus().registerObject(QStringLiteral("/notifications/debug"),
- this, QDBusConnection::ExportScriptableSlots |
- QDBusConnection::ExportScriptableSignals);
-
- const QString serverConfigFile = StandardDirs::serverConfigFile(XdgBaseDirs::ReadWrite);
- QSettings settings(serverConfigFile, QSettings::IniFormat);
-
- mTimer.setInterval(settings.value(QStringLiteral("NotificationManager/Interval"), 50).toInt());
- mTimer.setSingleShot(true);
- connect(&mTimer, &QTimer::timeout, this, &NotificationManager::emitPendingNotifications);
}
NotificationManager::~NotificationManager()
{
}
-NotificationManager *NotificationManager::self()
+void NotificationManager::init()
{
- if (!mSelf) {
- mSelf = new NotificationManager();
- }
+ AkThread::init();
- return mSelf;
+ const QString serverConfigFile = StandardDirs::serverConfigFile(XdgBaseDirs::ReadWrite);
+ QSettings settings(serverConfigFile, QSettings::IniFormat);
+
+ mTimer = new QTimer(this);
+ mTimer->setInterval(settings.value(QStringLiteral("NotificationManager/Interval"), 50).toInt());
+ mTimer->setSingleShot(true);
+ connect(mTimer, &QTimer::timeout,
+ this, &NotificationManager::emitPendingNotifications);
+
+ mNotifyThreadPool = new QThreadPool(this);
+ mNotifyThreadPool->setMaxThreadCount(5);
}
-void NotificationManager::connectNotificationCollector(NotificationCollector *collector)
+void NotificationManager::quit()
{
- connect(collector, &NotificationCollector::notify,
- this, &NotificationManager::slotNotify);
+ AkThread::quit();
+
+ qDeleteAll(mSubscribers);
}
-void NotificationManager::registerConnection(Connection *connection)
+void NotificationManager::registerConnection(quintptr socketDescriptor)
{
- QMutexLocker locker(&mSourcesLock);
- auto source = std::find_if(mNotificationSources.cbegin(), mNotificationSources.cend(),
- [connection](NotificationSource *source) {
- return connection->sessionId() == source->dbusPath().path().toLatin1();
- });
- if (source == mNotificationSources.cend()) {
- qWarning() << "Received request to register Notification bus connection, but there's no such subscriber";
- return;
- }
+ Q_ASSERT(thread() == QThread::currentThread());
- connect(const_cast<NotificationSource*>(*source), &NotificationSource::notify,
- connection, static_cast<void(Connection::*)(const Protocol::Command &)>(&Connection::sendResponse),
- Qt::QueuedConnection);
+ NotificationSubscriber *subscriber = new NotificationSubscriber(this, socketDescriptor);
+ qDebug() << "NotificationManager: new connection (registered as" << subscriber << ")";
+ connect(subscriber, &QObject::destroyed,
+ [this, subscriber]() {
+ mSubscribers.removeOne(subscriber);
+ });
+
+ mSubscribers.push_back(subscriber);
}
-void NotificationManager::unregisterConnection(Connection *connection)
+void NotificationManager::connectNotificationCollector(NotificationCollector *collector)
{
- QMutexLocker locker(&mSourcesLock);
- auto source = std::find_if(mNotificationSources.cbegin(), mNotificationSources.cend(),
- [connection](NotificationSource *source) {
- return connection->sessionId() == source->dbusPath().path().toLatin1();
- });
- if (source != mNotificationSources.cend()) {
- (*source)->disconnect(connection);
- }
+ connect(collector, &NotificationCollector::notify,
+ this, &NotificationManager::slotNotify);
}
-
-
-void NotificationManager::slotNotify(const Akonadi::Protocol::ChangeNotification::List &msgs)
+void NotificationManager::slotNotify(const Protocol::ChangeNotification::List &msgs)
{
- //qCDebug(AKONADISERVER_LOG) << Q_FUNC_INFO << "Appending" << msgs.count() << "notifications to current list of " << mNotifications.count() << "notifications";
Q_FOREACH (const Protocol::ChangeNotification &msg, msgs) {
- Protocol::ChangeNotification::appendAndCompress(mNotifications, msg);
+ if (msg.type() == Protocol::Command::CollectionChangeNotification) {
+ Protocol::CollectionChangeNotification::appendAndCompress(mNotifications, msg);
+ } else {
+ mNotifications.push_back(msg);
+ }
}
- //qCDebug(AKONADISERVER_LOG) << Q_FUNC_INFO << "We have" << mNotifications.count() << "notifications queued in total after appendAndCompress()";
- if (!mTimer.isActive()) {
- mTimer.start();
+ if (!mTimer->isActive()) {
+ mTimer->start();
}
}
-void NotificationManager::emitPendingNotifications()
+class NotifyRunnable : public QRunnable
{
- if (mNotifications.isEmpty()) {
- return;
+public:
+ explicit NotifyRunnable(NotificationSubscriber *subscriber,
+ const Protocol::ChangeNotification::List &notifications)
+ : mSubscriber(subscriber)
+ , mNotifications(notifications)
+ {
}
- if (mDebug) {
- QVector<QByteArray> bas;
- bas.reserve(mNotifications.size());
- QBuffer buffer;
- buffer.open(QIODevice::WriteOnly);
- Q_FOREACH (const Protocol::ChangeNotification &notification, mNotifications) {
- Tracer::self()->signal("NotificationManager::notify", notification.debugString());
- Protocol::serialize(&buffer, notification);
- bas << buffer.data();
- buffer.buffer().clear();
- buffer.seek(0);
- }
- Q_EMIT debugNotify(bas);
- } else {
- Q_FOREACH (const Protocol::ChangeNotification &notification, mNotifications) {
- Tracer::self()->signal("NotificationManager::notify", notification.debugString());
- }
+ ~NotifyRunnable()
+ {
}
- Q_FOREACH (NotificationSource *source, mNotificationSources) {
- Protocol::ChangeNotification::List acceptedNotifications;
- Q_FOREACH (const Protocol::ChangeNotification &notification, mNotifications) {
- if (source->acceptsNotification(notification)) {
- acceptedNotifications << notification;
- }
+ void run() Q_DECL_OVERRIDE
+ {
+ if (mSubscriber) {
+ mSubscriber->notify(mNotifications);
}
-
- if (!acceptedNotifications.isEmpty()) {
- source->emitNotification(acceptedNotifications);
- }
- }
-
- mNotifications.clear();
-}
-
-QDBusObjectPath NotificationManager::subscribe(const QString &identifier, bool exclusive)
-{
- qCDebug(AKONADISERVER_LOG) << Q_FUNC_INFO << this << identifier << exclusive;
- NotificationSource *source = mNotificationSources.value(identifier);
- if (source) {
- qCDebug(AKONADISERVER_LOG) << "Known subscriber" << identifier << "subscribes again";
- source->addClientServiceName(message().service());
- } else {
- source = new NotificationSource(identifier, message().service(), this);
}
- registerSource(source);
- source->setExclusive(exclusive);
+private:
+ QPointer<NotificationSubscriber> mSubscriber;
+ Protocol::ChangeNotification::List mNotifications;
+};
- // FIXME KF5: Emit the QDBusObjectPath instead of the identifier
- Q_EMIT subscribed(source->dbusPath());
-
- return source->dbusPath();
-}
-
-void NotificationManager::registerSource(NotificationSource *source)
-{
- // Protect write operations because of registerConnection()
- QMutexLocker locker(&mSourcesLock);
- mNotificationSources.insert(source->identifier(), source);
-}
-
-void NotificationManager::unsubscribe(const QString &identifier)
+void NotificationManager::emitPendingNotifications()
{
- NotificationSource *source = mNotificationSources.value(identifier);
- if (source) {
- unregisterSource(source);
- source->deleteLater();
- Q_EMIT unsubscribed(source->dbusPath());
- } else {
- qCDebug(AKONADISERVER_LOG) << "Attempt to unsubscribe unknown subscriber" << identifier;
+ if (mNotifications.isEmpty()) {
+ return;
}
-}
-void NotificationManager::unregisterSource(NotificationSource *source)
-{
- // Protect write operations because of registerConnection()
- QMutexLocker locker(&mSourcesLock);
- mNotificationSources.remove(source->identifier());
-}
-
-QList<QDBusObjectPath> NotificationManager::subscribers() const
-{
- QList<QDBusObjectPath> identifiers;
- identifiers.reserve(mNotificationSources.count());
- Q_FOREACH (NotificationSource *source, mNotificationSources) {
- identifiers << source->dbusPath();
+ Q_FOREACH (NotificationSubscriber *subscriber, mSubscribers) {
+ mNotifyThreadPool->start(new NotifyRunnable(subscriber, mNotifications));
}
- return identifiers;
-}
-
-void NotificationManager::enableDebug(bool enable)
-{
- mDebug = enable;
-}
-
-bool NotificationManager::debugEnabled() const
-{
- return mDebug;
+ mNotifications.clear();
}
diff --git a/src/server/notificationmanager.h b/src/server/notificationmanager.h
index 079f52e..69bc0e4 100644
--- a/src/server/notificationmanager.h
+++ b/src/server/notificationmanager.h
@@ -20,100 +20,52 @@
#ifndef AKONADI_NOTIFICATIONMANAGER_H
#define AKONADI_NOTIFICATIONMANAGER_H
+#include "akthread.h"
+
#include <private/protocol_p.h>
-#include "storage/entity.h"
-#include <QtCore/QHash>
-#include <QtCore/QObject>
#include <QtCore/QTimer>
-#include <QtCore/QMutex>
-#include <QtDBus/QDBusContext>
-#include <QtDBus/QDBusObjectPath>
class NotificationManagerTest;
+class QLocalSocket;
+class QThreadPool;
namespace Akonadi {
namespace Server {
class NotificationCollector;
-class NotificationSource;
-class Connection;
+class NotificationSubscriber;
-/**
- Notification manager D-Bus interface.
-*/
-class NotificationManager : public QObject, protected QDBusContext
+class NotificationManager : public AkThread
{
Q_OBJECT
- Q_CLASSINFO("D-Bus Interface", "org.freedesktop.Akonadi.NotificationManager")
public:
- static NotificationManager *self();
-
+ explicit NotificationManager();
virtual ~NotificationManager();
void connectNotificationCollector(NotificationCollector *collector);
- void registerConnection(Connection *connection);
- void unregisterConnection(Connection *connection);
-
public Q_SLOTS:
- Q_SCRIPTABLE void emitPendingNotifications();
-
- /**
- * Subscribe to notifications emitted by this manager.
- *
- * @param identifier Identifier to use for our subscription.
- * @param exclusive Exclusive subscribers also receive notifications on referenced collections
- * @return The path we got assigned. Contains identifier.
- */
- QDBusObjectPath subscribe(const QString &identifier, bool exclusive);
-
- /**
- * Unsubscribe from this manager.
- *
- * This method is for your inconvenience only. It's advisable to use the unsubscribe method
- * provided by the NotificationSource.
- *
- * @param identifier The identifier used for subscription.
- */
- void unsubscribe(const QString &identifier);
-
- /**
- * Returns identifiers of currently subscribed sources
- */
- Q_SCRIPTABLE QList<QDBusObjectPath> subscribers() const;
-
- Q_SCRIPTABLE void enableDebug(bool enable);
- Q_SCRIPTABLE bool debugEnabled() const;
-
-Q_SIGNALS:
- Q_SCRIPTABLE void debugNotify(const QVector<QByteArray> &msg);
-
- void subscribed(const QDBusObjectPath &path);
- void unsubscribed(const QDBusObjectPath &path);
+ void registerConnection(quintptr socketDescriptor);
+
+ void emitPendingNotifications();
private Q_SLOTS:
void slotNotify(const Akonadi::Protocol::ChangeNotification::List &msgs);
-private:
- NotificationManager();
+protected:
+ void init() Q_DECL_OVERRIDE;
+ void quit() Q_DECL_OVERRIDE;
private:
- void registerSource(NotificationSource *source);
- void unregisterSource(NotificationSource *source);
-
- static NotificationManager *mSelf;
Protocol::ChangeNotification::List mNotifications;
- QTimer mTimer;
-
- //! One message source for each subscribed process
- QMutex mSourcesLock;
- QHash<QString, NotificationSource *> mNotificationSources;
+ QTimer *mTimer;
- bool mDebug;
+ QThreadPool *mNotifyThreadPool;
+ QVector<NotificationSubscriber *> mSubscribers;
- friend class NotificationSource;
+ friend class NotificationSubscriber;
friend class ::NotificationManagerTest;
};
diff --git a/src/server/notificationsource.cpp b/src/server/notificationsource.cpp
deleted file mode 100644
index 14986d6..0000000
--- a/src/server/notificationsource.cpp
+++ /dev/null
@@ -1,483 +0,0 @@
-/*
- Copyright (c) 2010 Michael Jansen <kde@michael-jansen>
-
- 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 "notificationsource.h"
-#include "akonadiserver_debug.h"
-#include "notificationsourceadaptor.h"
-#include "notificationmanager.h"
-#include "collectionreferencemanager.h"
-#include "connection.h"
-
-using namespace Akonadi;
-using namespace Akonadi::Server;
-
-template<typename T>
-QVector<T> setToVector(const QSet<T> &set)
-{
- QVector<T> v;
- v.reserve(set.size());
- Q_FOREACH (const T &val, set) {
- v << val;
- }
- return v;
-}
-
-NotificationSource::NotificationSource(const QString &identifier, const QString &clientServiceName, NotificationManager *parent)
- : QObject(parent)
- , mManager(parent)
- , mIdentifier(identifier)
- , mDBusIdentifier(identifier)
- , mClientWatcher(0)
- , mAllMonitored(false)
- , mExclusive(false)
-{
- new NotificationSourceAdaptor(this);
-
- // Clean up for dbus usage: any non-alphanumeric char should be turned into '_'
- const int len = mDBusIdentifier.length();
- for (int i = 0; i < len; ++i) {
- if (!mDBusIdentifier[i].isLetterOrNumber()) {
- mDBusIdentifier[i] = QLatin1Char('_');
- }
- }
-
- QDBusConnection::sessionBus().registerObject(
- dbusPath().path(),
- this,
- QDBusConnection::ExportAdaptors);
-
- mClientWatcher = new QDBusServiceWatcher(clientServiceName, QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForUnregistration, this);
- connect(mClientWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &NotificationSource::serviceUnregistered);
-}
-
-NotificationSource::~NotificationSource()
-{
-}
-
-QDBusObjectPath NotificationSource::dbusPath() const
-{
- return QDBusObjectPath(QLatin1String("/subscriber/") + mDBusIdentifier);
-}
-
-void NotificationSource::emitNotification(const Protocol::ChangeNotification::List &notifications)
-{
- Q_FOREACH (const auto &notification, notifications) {
- Q_EMIT notify(notification);
- }
-}
-
-QString NotificationSource::identifier() const
-{
- return mIdentifier;
-}
-
-void NotificationSource::unsubscribe()
-{
- mManager->unsubscribe(mIdentifier);
-}
-
-bool NotificationSource::isExclusive() const
-{
- return mExclusive;
-}
-
-void NotificationSource::setExclusive(bool enabled)
-{
- mExclusive = enabled;
-}
-
-void NotificationSource::addClientServiceName(const QString &clientServiceName)
-{
- if (mClientWatcher->watchedServices().contains(clientServiceName)) {
- return;
- }
-
- mClientWatcher->addWatchedService(clientServiceName);
- qCDebug(AKONADISERVER_LOG) << Q_FUNC_INFO << "Notification source" << mIdentifier << "now serving:" << mClientWatcher->watchedServices();
-}
-
-void NotificationSource::serviceUnregistered(const QString &serviceName)
-{
- mClientWatcher->removeWatchedService(serviceName);
- qCDebug(AKONADISERVER_LOG) << Q_FUNC_INFO << "Notification source" << mIdentifier << "now serving:" << mClientWatcher->watchedServices();
-
- if (mClientWatcher->watchedServices().isEmpty()) {
- unsubscribe();
- }
-}
-
-void NotificationSource::setMonitoredCollection(Entity::Id id, bool monitored)
-{
- if (id < 0) {
- return;
- }
-
- if (monitored && !mMonitoredCollections.contains(id)) {
- mMonitoredCollections.insert(id);
- Q_EMIT monitoredCollectionsChanged();
- } else if (!monitored) {
- mMonitoredCollections.remove(id);
- Q_EMIT monitoredCollectionsChanged();
- }
-}
-
-QVector<Entity::Id> NotificationSource::monitoredCollections() const
-{
- return setToVector<Entity::Id>(mMonitoredCollections);
-}
-
-void NotificationSource::setMonitoredItem(Entity::Id id, bool monitored)
-{
- if (id < 0) {
- return;
- }
-
- if (monitored && !mMonitoredItems.contains(id)) {
- mMonitoredItems.insert(id);
- Q_EMIT monitoredItemsChanged();
- } else if (!monitored) {
- mMonitoredItems.remove(id);
- Q_EMIT monitoredItemsChanged();
- }
-}
-
-QVector<Entity::Id> NotificationSource::monitoredItems() const
-{
- return setToVector<Entity::Id>(mMonitoredItems);
-}
-
-void NotificationSource::setMonitoredTag(Entity::Id id, bool monitored)
-{
- if (id < 0) {
- return;
- }
-
- if (monitored && !mMonitoredTags.contains(id)) {
- mMonitoredTags.insert(id);
- Q_EMIT monitoredTagsChanged();
- } else if (!monitored) {
- mMonitoredTags.remove(id);
- Q_EMIT monitoredTagsChanged();
- }
-}
-
-QVector<Entity::Id> NotificationSource::monitoredTags() const
-{
- return setToVector<Entity::Id>(mMonitoredTags);
-}
-
-void NotificationSource::setMonitoredResource(const QByteArray &resource, bool monitored)
-{
- if (monitored && !mMonitoredResources.contains(resource)) {
- mMonitoredResources.insert(resource);
- Q_EMIT monitoredResourcesChanged();
- } else if (!monitored) {
- mMonitoredResources.remove(resource);
- Q_EMIT monitoredResourcesChanged();
- }
-}
-
-QVector<QByteArray> NotificationSource::monitoredResources() const
-{
- return setToVector<QByteArray>(mMonitoredResources);
-}
-
-void NotificationSource::setMonitoredMimeType(const QString &mimeType, bool monitored)
-{
- if (mimeType.isEmpty()) {
- return;
- }
-
- if (monitored && !mMonitoredMimeTypes.contains(mimeType)) {
- mMonitoredMimeTypes.insert(mimeType);
- Q_EMIT monitoredMimeTypesChanged();
- } else if (!monitored) {
- mMonitoredMimeTypes.remove(mimeType);
- Q_EMIT monitoredMimeTypesChanged();
- }
-}
-
-QStringList NotificationSource::monitoredMimeTypes() const
-{
- return mMonitoredMimeTypes.toList();
-}
-
-void NotificationSource::setAllMonitored(bool allMonitored)
-{
- if (allMonitored && !mAllMonitored) {
- mAllMonitored = true;
- Q_EMIT isAllMonitoredChanged();
- } else if (!allMonitored) {
- mAllMonitored = false;
- Q_EMIT isAllMonitoredChanged();
- }
-}
-
-bool NotificationSource::isAllMonitored() const
-{
- return mAllMonitored;
-}
-
-void NotificationSource::setSession(const QByteArray &sessionId)
-{
- mSession = sessionId;
-}
-
-void NotificationSource::setIgnoredSession(const QByteArray &sessionId, bool ignored)
-{
- if (ignored && !mIgnoredSessions.contains(sessionId)) {
- mIgnoredSessions.insert(sessionId);
- Q_EMIT ignoredSessionsChanged();
- } else if (!ignored) {
- mIgnoredSessions.remove(sessionId);
- Q_EMIT ignoredSessionsChanged();
- }
-}
-
-QVector<QByteArray> NotificationSource::ignoredSessions() const
-{
- return setToVector<QByteArray>(mIgnoredSessions);
-}
-
-bool NotificationSource::isCollectionMonitored(Entity::Id id) const
-{
- if (id < 0) {
- return false;
- } else if (mMonitoredCollections.contains(id)) {
- return true;
- } else if (mMonitoredCollections.contains(0)) {
- return true;
- }
- return false;
-}
-
-bool NotificationSource::isMimeTypeMonitored(const QString &mimeType) const
-{
- return mMonitoredMimeTypes.contains(mimeType);
-
- // FIXME: Handle mimetype aliases
-}
-
-bool NotificationSource::isMoveDestinationResourceMonitored(const Protocol::ChangeNotification &msg) const
-{
- if (msg.operation() != Protocol::ChangeNotification::Move) {
- return false;
- }
- return mMonitoredResources.contains(msg.destinationResource());
-}
-
-void NotificationSource::setMonitoredType(Protocol::ChangeNotification::Type type, bool monitored)
-{
- if (monitored && !mMonitoredTypes.contains(type)) {
- mMonitoredTypes.insert(type);
- Q_EMIT monitoredTypesChanged();
- } else if (!monitored) {
- mMonitoredTypes.remove(type);
- Q_EMIT monitoredTypesChanged();
- }
-}
-
-QVector<Protocol::ChangeNotification::Type> NotificationSource::monitoredTypes() const
-{
- return setToVector<Protocol::ChangeNotification::Type>(mMonitoredTypes);
-}
-
-bool NotificationSource::acceptsNotification(const Protocol::ChangeNotification &notification)
-{
- // session is ignored
- if (mIgnoredSessions.contains(notification.sessionId())) {
- return false;
- }
-
- if (notification.entities().count() == 0 && notification.type() != Protocol::ChangeNotification::Relations) {
- return false;
- }
-
- //Only emit notifications for referenced collections if the subscriber is exclusive or monitors the collection
- if (notification.type() == Protocol::ChangeNotification::Collections) {
- // HACK: We need to dispatch notifications about disabled collections to SOME
- // agents (that's what we have the exclusive subscription for) - but because
- // querying each Collection from database would be expensive, we use the
- // metadata hack to transfer this information from NotificationCollector
- if (notification.metadata().contains("DISABLED") && (notification.operation() != Protocol::ChangeNotification::Unsubscribe) && !notification.itemParts().contains("ENABLED")) {
- // Exclusive subscriber always gets it
- if (mExclusive) {
- return true;
- }
-
-
- Q_FOREACH (const Protocol::ChangeNotification::Entity &entity, notification.entities()) {
- //Deliver the notification if referenced from this session
- if (CollectionReferenceManager::instance()->isReferenced(entity.id, mSession)) {
- return true;
- }
- //Exclusive subscribers still want the notification
- if (mExclusive && CollectionReferenceManager::instance()->isReferenced(entity.id)) {
- return true;
- }
- }
-
- //The session belonging to this monitor referenced or dereferenced the collection. We always want this notification.
- //The referencemanager no longer holds a reference, so we have to check this way.
- if (notification.itemParts().contains("REFERENCED") && mSession == notification.sessionId()) {
- return true;
- }
-
- // If the collection is not referenced, monitored or the subscriber is not
- // exclusive (i.e. if we got here), then the subscriber does not care about
- // this one, so drop it
- return false;
- }
- } else if (notification.type() == Protocol::ChangeNotification::Items) {
- //We always want notifications that affect the parent resource (like an item added to a referenced collection)
- const bool notificationForParentResource = (mSession == notification.resource());
- if (CollectionReferenceManager::instance()->isReferenced(notification.parentCollection())) {
- return (mExclusive || isCollectionMonitored(notification.parentCollection()) || isMoveDestinationResourceMonitored(notification) || notificationForParentResource);
- }
- } else if (notification.type() == Protocol::ChangeNotification::Tags) {
- // Special handling for Tag removal notifications: When a Tag is removed,
- // a notification is emitted for each Resource that owns the tag (i.e.
- // each resource that owns a Tag RID - Tag RIDs are resource-specific).
- // Additionally then we send one more notification without any RID that is
- // destined for regular applications (which don't know anything about Tag RIDs)
- if (notification.operation() == Protocol::ChangeNotification::Remove) {
- // HACK: Since have no way to determine which resource this NotificationSource
- // belongs to, we are abusing the fact that each resource ignores it's own
- // main session, which is called the same name as the resource.
-
- // If there are any ignored sessions, but this notification does not have
- // a specific resource set, then we ignore it, as this notification is
- // for clients, not resources (does not have tag RID)
- if (!mIgnoredSessions.isEmpty() && notification.resource().isEmpty()) {
- return false;
- }
-
- // If this source ignores a session (i.e. we assume it is a resource),
- // but this notification is for another resource, then we ignore it
- if (!notification.resource().isEmpty() && !mIgnoredSessions.contains(notification.resource())) {
- return false;
- }
-
- // Now we got here, which means that this notification either has empty
- // resource, i.e. it is destined for a client applications, or it's
- // destined for resource that we *think* (see the hack above) this
- // NotificationSource belongs too. Which means we approve this notification,
- // but it can still be discarded in the generic Tag notification filter
- // below
- }
- }
-
- // user requested everything
- if (mAllMonitored && notification.type() != Protocol::ChangeNotification::InvalidType) {
- return true;
- }
-
- switch (notification.type()) {
- case Protocol::ChangeNotification::InvalidType:
- qCDebug(AKONADISERVER_LOG) << "Received invalid change notification!";
- return false;
-
- case Protocol::ChangeNotification::Items:
- if (!mMonitoredTypes.isEmpty() && !mMonitoredTypes.contains(Protocol::ChangeNotification::Items)) {
- return false;
- }
- // we have a resource or mimetype filter
- if (!mMonitoredResources.isEmpty() || !mMonitoredMimeTypes.isEmpty()) {
- if (mMonitoredResources.contains(notification.resource())) {
- return true;
- }
-
- if (isMoveDestinationResourceMonitored(notification)) {
- return true;
- }
-
- Q_FOREACH (const Protocol::ChangeNotification::Entity &entity, notification.entities()) {
- if (isMimeTypeMonitored(entity.mimeType)) {
- return true;
- }
- }
-
- return false;
- }
-
- // we explicitly monitor that item or the collections it's in
- Q_FOREACH (const Protocol::ChangeNotification::Entity &entity, notification.entities()) {
- if (mMonitoredItems.contains(entity.id)) {
- return true;
- }
- }
-
- return isCollectionMonitored(notification.parentCollection())
- || isCollectionMonitored(notification.parentDestCollection());
-
- case Protocol::ChangeNotification::Collections:
- if (!mMonitoredTypes.isEmpty() && !mMonitoredTypes.contains(Protocol::ChangeNotification::Collections)) {
- return false;
- }
-
- // we have a resource filter
- if (!mMonitoredResources.isEmpty()) {
- const bool resourceMatches = mMonitoredResources.contains(notification.resource())
- || isMoveDestinationResourceMonitored(notification);
-
- // a bit hacky, but match the behaviour from the item case,
- // if resource is the only thing we are filtering on, stop here, and if the resource filter matched, of course
- if (mMonitoredMimeTypes.isEmpty() || resourceMatches) {
- return resourceMatches;
- }
- // else continue
- }
-
- // we explicitly monitor that colleciton, or all of them
- Q_FOREACH (const Protocol::ChangeNotification::Entity &entity, notification.entities()) {
- if (isCollectionMonitored(entity.id)) {
- return true;
- }
- }
-
- return isCollectionMonitored(notification.parentCollection())
- || isCollectionMonitored(notification.parentDestCollection());
-
- case Protocol::ChangeNotification::Tags:
- if (!mMonitoredTypes.isEmpty() && !mMonitoredTypes.contains(Protocol::ChangeNotification::Tags)) {
- return false;
- }
-
- if (mMonitoredTags.isEmpty()) {
- return true;
- }
-
- Q_FOREACH (const Protocol::ChangeNotification::Entity &entity, notification.entities()) {
- if (mMonitoredTags.contains(entity.id)) {
- return true;
- }
- }
-
- return false;
-
- case Protocol::ChangeNotification::Relations:
- if (!mMonitoredTypes.isEmpty() && !mMonitoredTypes.contains(Protocol::ChangeNotification::Relations)) {
- return false;
- }
- return true;
-
- }
-
- return false;
-}
diff --git a/src/server/notificationsource.h b/src/server/notificationsource.h
deleted file mode 100644
index 72acc98..0000000
--- a/src/server/notificationsource.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- Copyright (c) 2010 Michael Jansen <kde@michael-jansen>
-
- 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.
-*/
-#ifndef AKONADI_NOTIFICATIONSOURCE_H
-#define AKONADI_NOTIFICATIONSOURCE_H
-
-#include <QtCore/QObject>
-#include <QtCore/QVector>
-#include <QtDBus/QtDBus>
-
-#include "entities.h"
-
-#include <private/protocol_p.h>
-
-namespace Akonadi {
-namespace Server {
-
-class Connection;
-class NotificationManager;
-
-class NotificationSource : public QObject
-{
- Q_OBJECT
- Q_CLASSINFO("D-Bus Interface", "org.freedesktop.Akonadi.NotificationSource")
-
-public:
-
- /**
- * Construct a NotificationSource.
- *
- * @param identifier The identifier of this notification source, defined by the client
- * @param clientServiceName The D-Bus service name of the client, used to clean up if the client does not unsubscribe correctly.
- * @param parent The parent object.
- */
- NotificationSource(const QString &identifier, const QString &clientServiceName, NotificationManager *parent);
-
- /**
- * Destroy the NotificationSource.
- */
- virtual ~NotificationSource();
-
- /**
- * Emit the given notifications
- *
- * @param notifications List of notifications to emit.
- */
- void emitNotification(const Protocol::ChangeNotification::List &notifications);
-
- /**
- * Return the dbus path this message source uses.
- */
- QDBusObjectPath dbusPath() const;
-
- /**
- * Return the identifier for this message source
- */
- QString identifier() const;
-
- /**
- * Add another client service to watch for. Auto-unsubscription only happens if
- * all watched client services have been stopped.
- */
- void addClientServiceName(const QString &clientServiceName);
-
- bool acceptsNotification(const Protocol::ChangeNotification &notification);
-
-public Q_SLOTS:
- /**
- * Unsubscribe from the message source.
- *
- * This will delete the message source and make the used dbus path unavailable.
- */
- Q_SCRIPTABLE void unsubscribe();
-
- Q_SCRIPTABLE void setMonitoredCollection(Entity::Id id, bool monitored);
- Q_SCRIPTABLE QVector<Entity::Id> monitoredCollections() const;
- Q_SCRIPTABLE void setMonitoredItem(Entity::Id id, bool monitored);
- Q_SCRIPTABLE QVector<Entity::Id> monitoredItems() const;
- Q_SCRIPTABLE void setMonitoredTag(Entity::Id id, bool monitored);
- Q_SCRIPTABLE QVector<Entity::Id> monitoredTags() const;
- Q_SCRIPTABLE void setMonitoredResource(const QByteArray &resource, bool monitored);
- Q_SCRIPTABLE QVector<QByteArray> monitoredResources() const;
- Q_SCRIPTABLE void setMonitoredMimeType(const QString &mimeType, bool monitored);
- Q_SCRIPTABLE QStringList monitoredMimeTypes() const;
- Q_SCRIPTABLE void setAllMonitored(bool allMonitored);
- Q_SCRIPTABLE bool isAllMonitored() const;
- Q_SCRIPTABLE void setSession( const QByteArray &sessionId );
- Q_SCRIPTABLE void setIgnoredSession(const QByteArray &sessionId, bool ignored);
- Q_SCRIPTABLE QVector<QByteArray> ignoredSessions() const;
- Q_SCRIPTABLE void setMonitoredType(Protocol::ChangeNotification::Type type, bool monitored);
- Q_SCRIPTABLE QVector<Protocol::ChangeNotification::Type> monitoredTypes() const;
- Q_SCRIPTABLE void setExclusive( bool exclusive );
- Q_SCRIPTABLE bool isExclusive() const;
-
-Q_SIGNALS:
- // Internal, not exported to DBus
- void notify(const Akonadi::Protocol::Command &response);
-
- Q_SCRIPTABLE void monitoredCollectionsChanged();
- Q_SCRIPTABLE void monitoredItemsChanged();
- Q_SCRIPTABLE void monitoredTagsChanged();
- Q_SCRIPTABLE void monitoredResourcesChanged();
- Q_SCRIPTABLE void monitoredMimeTypesChanged();
- Q_SCRIPTABLE void isAllMonitoredChanged();
- Q_SCRIPTABLE void ignoredSessionsChanged();
- Q_SCRIPTABLE void monitoredTypesChanged();
-
-private Q_SLOTS:
- void serviceUnregistered(const QString &serviceName);
-
-private:
- bool isCollectionMonitored(Entity::Id id) const;
- bool isMimeTypeMonitored(const QString &mimeType) const;
- bool isMoveDestinationResourceMonitored(const Protocol::ChangeNotification &msg) const;
-
-private:
- NotificationManager *mManager;
- QString mIdentifier;
- QString mDBusIdentifier;
- QDBusServiceWatcher *mClientWatcher;
-
- QPointer<Connection> mConnection;
-
- bool mAllMonitored;
- bool mExclusive;
- QSet<Entity::Id> mMonitoredCollections;
- QSet<Entity::Id> mMonitoredItems;
- QSet<Entity::Id> mMonitoredTags;
- // TODO: Make this a bitflag
- QSet<Protocol::ChangeNotification::Type> mMonitoredTypes;
- QSet<QString> mMonitoredMimeTypes;
- QSet<QByteArray> mMonitoredResources;
- QSet<QByteArray> mIgnoredSessions;
- QByteArray mSession;
-
-}; // class NotificationSource
-
-} // namespace Server
-} // namespace Akonadi
-
-#endif // #define AKONADI_NOTIFICATIONSOURCE_H
diff --git a/src/server/notificationsubscriber.cpp b/src/server/notificationsubscriber.cpp
new file mode 100644
index 0000000..376d5c3
--- /dev/null
+++ b/src/server/notificationsubscriber.cpp
@@ -0,0 +1,597 @@
+/*
+ Copyright (c) 2015 Daniel Vrátil <dvratil@kde.org>
+
+ 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 "notificationsubscriber.h"
+#include "akonadiserver_debug.h"
+#include "notificationmanager.h"
+#include "collectionreferencemanager.h"
+
+#include <QLocalSocket>
+#include <QDataStream>
+
+#include <private/protocol_p.h>
+#include <private/protocol_exception_p.h>
+
+using namespace Akonadi;
+using namespace Akonadi::Server;
+
+QMimeDatabase NotificationSubscriber::sMimeDatabase;
+
+NotificationSubscriber::NotificationSubscriber(NotificationManager *manager)
+ : QObject()
+ , mManager(manager)
+ , mSocket(Q_NULLPTR)
+ , mAllMonitored(false)
+ , mExclusive(false)
+{
+}
+
+
+NotificationSubscriber::NotificationSubscriber(NotificationManager *manager, quintptr socketDescriptor)
+ : NotificationSubscriber(manager)
+{
+ mSocket = new QLocalSocket(this);
+ connect(mSocket, &QLocalSocket::readyRead,
+ this, &NotificationSubscriber::socketReadyRead);
+ connect(mSocket, &QLocalSocket::disconnected,
+ this, &NotificationSubscriber::socketDisconnected);
+ mSocket->setSocketDescriptor(socketDescriptor);
+
+ writeCommand(0, Protocol::HelloResponse(QStringLiteral("Akonadi"),
+ QStringLiteral("Not-really IMAP server"),
+ Protocol::version()));
+}
+
+NotificationSubscriber::~NotificationSubscriber()
+{
+}
+
+void NotificationSubscriber::socketReadyRead()
+{
+ while (mSocket->bytesAvailable() > (int) sizeof(qint64)) {
+ QDataStream stream(mSocket);
+
+ // Ignored atm
+ qint64 tag = -1;
+ stream >> tag;
+
+ Protocol::Command cmd;
+ try {
+ cmd = Protocol::deserialize(mSocket);
+ } catch (const Akonadi::ProtocolException &e) {
+ qDebug() << "ProtocolException:" << e.what();
+ disconnectSubscriber();
+ return;
+ } catch (const std::exception &e) {
+ qDebug() << "Unknown exception:" << e.what();
+ disconnectSubscriber();
+ return;
+ }
+ if (cmd.type() == Protocol::Command::Invalid) {
+ qDebug() << "Received an invalid command: resetting connection";
+ disconnectSubscriber();
+ return;
+ }
+
+ switch (cmd.type()) {
+ case Protocol::Command::CreateSubscription:
+ registerSubscriber(cmd);
+ writeCommand(tag, Protocol::CreateSubscriptionResponse());
+ break;
+ case Protocol::Command::ModifySubscription:
+ if (mSubscriber.isEmpty()) {
+ qDebug() << "Received ModifySubscription command before RegisterSubscriber";
+ disconnectSubscriber();
+ return;
+ }
+ modifySubscription(cmd);
+ writeCommand(tag, Protocol::ModifySubscriptionResponse());
+ break;
+ case Protocol::Command::Logout:
+ disconnectSubscriber();
+ break;
+ default:
+ qDebug() << "Invalid command" << cmd.type() << "received by NotificationSubscriber" << mSubscriber;
+ disconnectSubscriber();
+ break;
+ }
+ }
+}
+
+void NotificationSubscriber::socketDisconnected()
+{
+ qDebug() << "Subscriber" << mSubscriber << "disconnected from us!";
+ disconnectSubscriber();
+}
+
+void NotificationSubscriber::disconnectSubscriber()
+{
+ QMutexLocker locker(&mLock);
+
+ if (mManager) {
+ Protocol::SubscriptionChangeNotification changeNtf;
+ changeNtf.setSubscriber(mSubscriber);
+ changeNtf.setSessionId(mSession);
+ changeNtf.setOperation(Protocol::SubscriptionChangeNotification::Remove);
+ mManager->slotNotify({ changeNtf });
+ }
+
+ disconnect(mSocket, &QLocalSocket::readyRead,
+ this, &NotificationSubscriber::socketReadyRead);
+ disconnect(mSocket, &QLocalSocket::disconnected,
+ this, &NotificationSubscriber::socketDisconnected);
+ mSocket->close();
+ deleteLater();
+}
+
+void NotificationSubscriber::registerSubscriber(const Protocol::CreateSubscriptionCommand &command)
+{
+ QMutexLocker locker(&mLock);
+
+ qDebug() << "Subscriber" << this << "identified as" << command.subscriberName();
+ mSubscriber = command.subscriberName();
+ mSession = command.session();
+
+ if (mManager) {
+ Protocol::SubscriptionChangeNotification changeNtf;
+ changeNtf.setSubscriber(mSubscriber);
+ changeNtf.setSessionId(mSession);
+ changeNtf.setOperation(Protocol::SubscriptionChangeNotification::Add);
+ mManager->slotNotify({ changeNtf });
+ }
+}
+
+void NotificationSubscriber::modifySubscription(const Protocol::ModifySubscriptionCommand &command)
+{
+ QMutexLocker locker(&mLock);
+
+ const auto modifiedParts = command.modifiedParts();
+
+ #define START_MONITORING(type) \
+ (modifiedParts & Protocol::ModifySubscriptionCommand::ModifiedParts( \
+ Protocol::ModifySubscriptionCommand::type | Protocol::ModifySubscriptionCommand::Add))
+ #define STOP_MONITORING(type) \
+ (modifiedParts & Protocol::ModifySubscriptionCommand::ModifiedParts( \
+ Protocol::ModifySubscriptionCommand::type | Protocol::ModifySubscriptionCommand::Remove))
+
+ #define APPEND(set, newItems) \
+ Q_FOREACH (const auto &entity, newItems) { \
+ set.insert(entity); \
+ }
+
+ #define REMOVE(set, items) \
+ Q_FOREACH (const auto &entity, items) { \
+ set.insert(entity); \
+ }
+
+ if (START_MONITORING(Types)) {
+ APPEND(mMonitoredTypes, command.startMonitoringTypes())
+ }
+ if (STOP_MONITORING(Types)) {
+ REMOVE(mMonitoredTypes, command.stopMonitoringTypes())
+ }
+ if (START_MONITORING(Collections)) {
+ APPEND(mMonitoredCollections, command.startMonitoringCollections())
+ }
+ if (STOP_MONITORING(Collections)) {
+ REMOVE(mMonitoredCollections, command.stopMonitoringCollections())
+ }
+ if (START_MONITORING(Items)) {
+ APPEND(mMonitoredItems, command.startMonitoringItems())
+ }
+ if (STOP_MONITORING(Items)) {
+ REMOVE(mMonitoredItems, command.stopMonitoringItems())
+ }
+ if (START_MONITORING(Tags)) {
+ APPEND(mMonitoredTags, command.startMonitoringTags())
+ }
+ if (STOP_MONITORING(Tags)) {
+ REMOVE(mMonitoredTags, command.stopMonitoringTags())
+ }
+ if (START_MONITORING(Resources)) {
+ APPEND(mMonitoredResources, command.startMonitoringResources())
+ }
+ if (STOP_MONITORING(Resources)) {
+ REMOVE(mMonitoredResources, command.stopMonitoringResources())
+ }
+ if (START_MONITORING(MimeTypes)) {
+ APPEND(mMonitoredMimeTypes, command.startMonitoringMimeTypes())
+ }
+ if (STOP_MONITORING(MimeTypes)) {
+ REMOVE(mMonitoredMimeTypes, command.stopMonitoringMimeTypes())
+ }
+ if (START_MONITORING(Sessions)) {
+ APPEND(mIgnoredSessions, command.startIgnoringSessions())
+ }
+ if (STOP_MONITORING(Sessions)) {
+ REMOVE(mIgnoredSessions, command.stopIgnoringSessions())
+ }
+ if (modifiedParts & Protocol::ModifySubscriptionCommand::AllFlag) {
+ mAllMonitored = command.allMonitored();
+ }
+ if (modifiedParts & Protocol::ModifySubscriptionCommand::ExclusiveFlag) {
+ mExclusive = command.isExclusive();
+ }
+
+ if (mManager) {
+ // Did the caller just subscribed to subscription changes?
+ if ((modifiedParts & Protocol::ModifySubscriptionCommand::Types)
+ && command.startMonitoringTypes().contains(Protocol::ModifySubscriptionCommand::SubscriptionChanges))
+ {
+ // If yes, then send them list of all existing subscribers
+ Protocol::ChangeNotification::List ntfs;
+ Q_FOREACH (const NotificationSubscriber *subscriber, mManager->mSubscribers) {
+ ntfs << subscriber->toChangeNotification();
+ }
+ // Send them back to caller
+ notify(ntfs);
+ }
+
+ // Emit subscription change notification
+ Protocol::SubscriptionChangeNotification changeNtf = toChangeNotification();
+ changeNtf.setOperation(Protocol::SubscriptionChangeNotification::Modify);
+ mManager->slotNotify({ changeNtf });
+ }
+}
+
+Protocol::ChangeNotification NotificationSubscriber::toChangeNotification() const
+{
+ // Assumes mLock being locked by caller
+
+ Protocol::SubscriptionChangeNotification ntf;
+ ntf.setSessionId(mSession);
+ ntf.setSubscriber(mSubscriber);
+ ntf.setOperation(Protocol::SubscriptionChangeNotification::Add);
+ ntf.setAddedCollections(mMonitoredCollections);
+ ntf.setAddedItems(mMonitoredItems);
+ ntf.setAddedTags(mMonitoredTags);
+ ntf.setAddedTypes(mMonitoredTypes);
+ ntf.setAddedMimeTypes(mMonitoredMimeTypes);
+ ntf.setAddedResources(mMonitoredResources);
+ ntf.setAddedIgnoredSessions(mIgnoredSessions);
+ ntf.setAllMonitored(mAllMonitored);
+ ntf.setExclusive(mExclusive);
+ return ntf;
+}
+
+
+
+bool NotificationSubscriber::isCollectionMonitored(Entity::Id id) const
+{
+ // Assumes mLock being locked by caller
+
+ if (id < 0) {
+ return false;
+ } else if (mMonitoredCollections.contains(id)) {
+ return true;
+ } else if (mMonitoredCollections.contains(0)) {
+ return true;
+ }
+ return false;
+}
+
+bool NotificationSubscriber::isMimeTypeMonitored(const QString &mimeType) const
+{
+ // Assumes mLock being locked by caller
+
+ const QMimeType mt = sMimeDatabase.mimeTypeForName(mimeType);
+ if (mMonitoredMimeTypes.contains(mimeType)) {
+ return true;
+ }
+
+ Q_FOREACH (const QString &alias, mt.aliases()) {
+ if (mMonitoredMimeTypes.contains(alias)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool NotificationSubscriber::isMoveDestinationResourceMonitored(const Protocol::ItemChangeNotification &msg) const
+{
+ // Assumes mLock being locked by caller
+
+ if (msg.operation() != Protocol::ItemChangeNotification::Move) {
+ return false;
+ }
+ return mMonitoredResources.contains(msg.destinationResource());
+}
+
+bool NotificationSubscriber::isMoveDestinationResourceMonitored(const Protocol::CollectionChangeNotification &msg) const
+{
+ // Assumes mLock being locked by caller
+
+ if (msg.operation() != Protocol::CollectionChangeNotification::Move) {
+ return false;
+ }
+ return mMonitoredResources.contains(msg.destinationResource());
+}
+
+
+bool NotificationSubscriber::acceptsItemNotification(const Protocol::ItemChangeNotification &notification) const
+{
+ // Assumes mLock being locked by caller
+
+ if (notification.items().count() == 0) {
+ return false;
+ }
+
+ if (CollectionReferenceManager::instance()->isReferenced(notification.parentCollection())) {
+ //We always want notifications that affect the parent resource (like an item added to a referenced collection)
+ const bool notificationForParentResource = (mSession == notification.resource());
+ return (mExclusive
+ || isCollectionMonitored(notification.parentCollection())
+ || isMoveDestinationResourceMonitored(notification)
+ || notificationForParentResource);
+ }
+
+
+ if (mAllMonitored) {
+ return true;
+ }
+
+ if (!mMonitoredTypes.isEmpty() && !mMonitoredTypes.contains(Protocol::ModifySubscriptionCommand::ItemChanges)) {
+ return false;
+ }
+
+ // we have a resource or mimetype filter
+ if (!mMonitoredResources.isEmpty() || !mMonitoredMimeTypes.isEmpty()) {
+ if (mMonitoredResources.contains(notification.resource())) {
+ return true;
+ }
+
+ if (isMoveDestinationResourceMonitored(notification)) {
+ return true;
+ }
+
+ Q_FOREACH (const auto &item, notification.items()) {
+ if (isMimeTypeMonitored(item.mimeType)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // we explicitly monitor that item or the collections it's in
+ Q_FOREACH (const auto &item, notification.items()) {
+ if (mMonitoredItems.contains(item.id)) {
+ return true;
+ }
+ }
+
+ return isCollectionMonitored(notification.parentCollection())
+ || isCollectionMonitored(notification.parentDestCollection());
+}
+
+bool NotificationSubscriber::acceptsCollectionNotification(const Protocol::CollectionChangeNotification &notification) const
+{
+ // Assumes mLock being locked by caller
+
+ if (notification.id() < 0) {
+ return false;
+ }
+
+ // HACK: We need to dispatch notifications about disabled collections to SOME
+ // agents (that's what we have the exclusive subscription for) - but because
+ // querying each Collection from database would be expensive, we use the
+ // metadata hack to transfer this information from NotificationCollector
+ if (notification.metadata().contains("DISABLED")
+ && (notification.operation() != Protocol::CollectionChangeNotification::Unsubscribe)
+ && !notification.changedParts().contains("ENABLED")) {
+ // Exclusive subscriber always gets it
+ if (mExclusive) {
+ return true;
+ }
+
+ //Deliver the notification if referenced from this session
+ if (CollectionReferenceManager::instance()->isReferenced(notification.id(), mSession)) {
+ return true;
+ }
+
+ //Exclusive subscribers still want the notification
+ if (mExclusive && CollectionReferenceManager::instance()->isReferenced(notification.id())) {
+ return true;
+ }
+
+ //The session belonging to this monitor referenced or dereferenced the collection. We always want this notification.
+ //The referencemanager no longer holds a reference, so we have to check this way.
+ if (notification.changedParts().contains(AKONADI_PARAM_REFERENCED) && mSession == notification.sessionId()) {
+ return true;
+ }
+
+ // If the collection is not referenced, monitored or the subscriber is not
+ // exclusive (i.e. if we got here), then the subscriber does not care about
+ // this one, so drop it
+ return false;
+ }
+
+ if (mAllMonitored) {
+ return true;
+ }
+
+ if (!mMonitoredTypes.isEmpty() && !mMonitoredTypes.contains(Protocol::ModifySubscriptionCommand::CollectionChanges)) {
+ return false;
+ }
+
+ // we have a resource filter
+ if (!mMonitoredResources.isEmpty()) {
+ const bool resourceMatches = mMonitoredResources.contains(notification.resource())
+ || isMoveDestinationResourceMonitored(notification);
+
+ // a bit hacky, but match the behaviour from the item case,
+ // if resource is the only thing we are filtering on, stop here, and if the resource filter matched, of course
+ if (mMonitoredMimeTypes.isEmpty() || resourceMatches) {
+ return resourceMatches;
+ }
+ // else continue
+ }
+
+ // we explicitly monitor that colleciton, or all of them
+ if (isCollectionMonitored(notification.id())) {
+ return true;
+ }
+
+ return isCollectionMonitored(notification.parentCollection())
+ || isCollectionMonitored(notification.parentDestCollection());
+
+}
+
+bool NotificationSubscriber::acceptsTagNotification(const Protocol::TagChangeNotification &notification) const
+{
+ // Assumes mLock being locked by caller
+
+ if (notification.id() < 0) {
+ return false;
+ }
+
+ // Special handling for Tag removal notifications: When a Tag is removed,
+ // a notification is emitted for each Resource that owns the tag (i.e.
+ // each resource that owns a Tag RID - Tag RIDs are resource-specific).
+ // Additionally then we send one more notification without any RID that is
+ // destined for regular applications (which don't know anything about Tag RIDs)
+ if (notification.operation() == Protocol::TagChangeNotification::Remove) {
+ // HACK: Since have no way to determine which resource this NotificationSource
+ // belongs to, we are abusing the fact that each resource ignores it's own
+ // main session, which is called the same name as the resource.
+
+ // If there are any ignored sessions, but this notification does not have
+ // a specific resource set, then we ignore it, as this notification is
+ // for clients, not resources (does not have tag RID)
+ if (!mIgnoredSessions.isEmpty() && notification.resource().isEmpty()) {
+ return false;
+ }
+
+ // If this source ignores a session (i.e. we assume it is a resource),
+ // but this notification is for another resource, then we ignore it
+ if (!notification.resource().isEmpty() && !mIgnoredSessions.contains(notification.resource())) {
+ return false;
+ }
+
+ // Now we got here, which means that this notification either has empty
+ // resource, i.e. it is destined for a client applications, or it's
+ // destined for resource that we *think* (see the hack above) this
+ // NotificationSource belongs too. Which means we approve this notification,
+ // but it can still be discarded in the generic Tag notification filter
+ // below
+ }
+
+ if (mAllMonitored) {
+ return true;
+ }
+
+ if (!mMonitoredTypes.isEmpty() && !mMonitoredTypes.contains(Protocol::ModifySubscriptionCommand::TagChanges)) {
+ return false;
+ }
+
+ if (mMonitoredTags.isEmpty()) {
+ return true;
+ }
+
+ if (mMonitoredTags.contains(notification.id())) {
+ return true;
+ }
+
+ return false;
+}
+
+bool NotificationSubscriber::acceptsRelationNotification(const Protocol::RelationChangeNotification &notification) const
+{
+ // Assumes mLock being locked by caller
+
+ Q_UNUSED(notification);
+
+ if (mAllMonitored) {
+ return true;
+ }
+
+ if (!mMonitoredTypes.isEmpty() && !mMonitoredTypes.contains(Protocol::ModifySubscriptionCommand::RelationChanges)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool NotificationSubscriber::acceptsSubscriptionNotification(const Protocol::SubscriptionChangeNotification &notification) const
+{
+ // Assumes mLock being locked by caller
+
+ Q_UNUSED(notification);
+
+ // Unlike other types, subscription notifications must be explicitly enabled
+ // by caller and are excluded from "monitor all" as well
+ return mMonitoredTypes.contains(Protocol::ModifySubscriptionCommand::SubscriptionChanges);
+}
+
+bool NotificationSubscriber::acceptsNotification(const Protocol::ChangeNotification &notification) const
+{
+ // Assumes mLock being locked
+
+ // Uninitialized subscriber gets nothing
+ if (mSubscriber.isEmpty()) {
+ return false;
+ }
+
+ // session is ignored
+ if (mIgnoredSessions.contains(notification.sessionId())) {
+ return false;
+ }
+
+ switch (notification.type()) {
+ case Protocol::Command::ItemChangeNotification:
+ return acceptsItemNotification(notification);
+ case Protocol::Command::CollectionChangeNotification:
+ return acceptsCollectionNotification(notification);
+ case Protocol::Command::TagChangeNotification:
+ return acceptsTagNotification(notification);
+ case Protocol::Command::RelationChangeNotification:
+ return acceptsRelationNotification(notification);
+ case Protocol::Command::SubscriptionChangeNotification:
+ return acceptsSubscriptionNotification(notification);
+ default:
+ qCDebug(AKONADISERVER_LOG) << "Received invalid change notification!";
+ return false;
+ }
+}
+
+void NotificationSubscriber::notify(const Protocol::ChangeNotification::List &notifications)
+{
+ QMutexLocker locker(&mLock);
+
+ Q_FOREACH (const auto &notification, notifications) {
+ if (acceptsNotification(notification)) {
+ QMetaObject::invokeMethod(this, "writeNotification", Qt::QueuedConnection,
+ Q_ARG(Akonadi::Protocol::ChangeNotification, notification));
+ }
+ }
+}
+
+void NotificationSubscriber::writeNotification(const Protocol::ChangeNotification &notification)
+{
+ // tag chosen by fair dice roll
+ writeCommand(4, notification);
+}
+
+void NotificationSubscriber::writeCommand(qint64 tag, const Protocol::Command& cmd)
+{
+ QDataStream stream(mSocket);
+ stream << tag;
+ Protocol::serialize(mSocket, cmd);
+}
diff --git a/src/server/notificationsubscriber.h b/src/server/notificationsubscriber.h
new file mode 100644
index 0000000..2e3af8e
--- /dev/null
+++ b/src/server/notificationsubscriber.h
@@ -0,0 +1,102 @@
+/*
+ Copyright (c) 2015 Daniel Vrátil <dvratil@kde.org>
+
+ 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.
+*/
+
+#ifndef NOTIFICATIONSUBSCRIBER_H
+#define NOTIFICATIONSUBSCRIBER_H
+
+#include <QObject>
+#include <QByteArray>
+#include <QMimeDatabase>
+#include <QMutex>
+
+#include <private/protocol_p.h>
+#include "entities.h"
+
+class QLocalSocket;
+
+namespace Akonadi {
+namespace Server {
+
+class NotificationManager;
+
+class NotificationSubscriber : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit NotificationSubscriber(NotificationManager *manager, quintptr socketDescriptor);
+ ~NotificationSubscriber();
+
+ void notify(const Protocol::ChangeNotification::List &notifications);
+
+private Q_SLOTS:
+ void socketReadyRead();
+ void socketDisconnected();
+
+protected:
+ void registerSubscriber(const Protocol::CreateSubscriptionCommand &command);
+ void modifySubscription(const Protocol::ModifySubscriptionCommand &command);
+ void disconnectSubscriber();
+
+private:
+ bool acceptsNotification(const Protocol::ChangeNotification &notification) const;
+ bool acceptsItemNotification(const Protocol::ItemChangeNotification &notification) const;
+ bool acceptsCollectionNotification(const Protocol::CollectionChangeNotification &notification) const;
+ bool acceptsTagNotification(const Protocol::TagChangeNotification &notification) const;
+ bool acceptsRelationNotification(const Protocol::RelationChangeNotification &notification) const;
+ bool acceptsSubscriptionNotification(const Protocol::SubscriptionChangeNotification &notification) const;
+
+ bool isCollectionMonitored(Entity::Id id) const;
+ bool isMimeTypeMonitored(const QString &mimeType) const;
+ bool isMoveDestinationResourceMonitored(const Protocol::ItemChangeNotification &msg) const;
+ bool isMoveDestinationResourceMonitored(const Protocol::CollectionChangeNotification &msg) const;
+
+ Protocol::ChangeNotification toChangeNotification() const;
+
+protected Q_SLOTS:
+ virtual void writeNotification(const Akonadi::Protocol::ChangeNotification &notification);
+
+protected:
+ explicit NotificationSubscriber(NotificationManager *manager = Q_NULLPTR);
+
+ void writeCommand(qint64 tag, const Protocol::Command &cmd);
+
+ mutable QMutex mLock;
+ NotificationManager *mManager;
+ QLocalSocket *mSocket;
+ QByteArray mSubscriber;
+ QSet<Entity::Id> mMonitoredCollections;
+ QSet<Entity::Id> mMonitoredItems;
+ QSet<Entity::Id> mMonitoredTags;
+ QSet<Protocol::ModifySubscriptionCommand::ChangeType> mMonitoredTypes;
+ QSet<QString> mMonitoredMimeTypes;
+ QSet<QByteArray> mMonitoredResources;
+ QSet<QByteArray> mIgnoredSessions;
+ QByteArray mSession;
+ bool mAllMonitored;
+ bool mExclusive;
+
+ static QMimeDatabase sMimeDatabase;
+};
+
+} // namespace Server
+} // namespace Akonadi
+
+
+#endif
diff --git a/src/server/search/searchmanager.cpp b/src/server/search/searchmanager.cpp
index c0253f1..d1ab843 100644
--- a/src/server/search/searchmanager.cpp
+++ b/src/server/search/searchmanager.cpp
@@ -117,10 +117,16 @@ void SearchManager::quit()
qDeleteAll(mEngines);
qDeleteAll(mPlugins);
+ /*
+ * FIXME: Unloading plugin messes up some global statics from client libs
+ * and causes crash on Akonadi shutdown (below main). Keeping the plugins
+ * loaded is not really a big issue as this is only invoked on server shutdown
+ * anyway, so we are not leaking any memory.
Q_FOREACH (QPluginLoader *loader, mPluginLoaders) {
loader->unload();
delete loader;
}
+ */
AkThread::quit();
}
diff --git a/src/server/storage/datastore.cpp b/src/server/storage/datastore.cpp
index e7a4770..80384d4 100644
--- a/src/server/storage/datastore.cpp
+++ b/src/server/storage/datastore.cpp
@@ -20,6 +20,7 @@
#include "datastore.h"
+#include "akonadi.h"
#include "dbconfig.h"
#include "dbinitializer.h"
#include "dbupdater.h"
@@ -204,7 +205,10 @@ NotificationCollector *DataStore::notificationCollector()
{
if (mNotificationCollector == 0) {
mNotificationCollector = new NotificationCollector(this);
- NotificationManager::self()->connectNotificationCollector(notificationCollector());
+ NotificationManager *notificationManager = AkonadiServer::instance()->notificationManager();
+ if (notificationManager) {
+ notificationManager->connectNotificationCollector(notificationCollector());
+ }
}
return mNotificationCollector;
diff --git a/src/server/storage/notificationcollector.cpp b/src/server/storage/notificationcollector.cpp
index 40ee0dc..63e0120 100644
--- a/src/server/storage/notificationcollector.cpp
+++ b/src/server/storage/notificationcollector.cpp
@@ -56,7 +56,7 @@ void NotificationCollector::itemAdded(const PimItem &item,
const QByteArray &resource)
{
SearchManager::instance()->scheduleSearchUpdate();
- itemNotification(Protocol::ChangeNotification::Add, item, collection, Collection(), resource);
+ itemNotification(Protocol::ItemChangeNotification::Add, item, collection, Collection(), resource);
}
void NotificationCollector::itemChanged(const PimItem &item,
@@ -65,7 +65,7 @@ void NotificationCollector::itemChanged(const PimItem &item,
const QByteArray &resource)
{
SearchManager::instance()->scheduleSearchUpdate();
- itemNotification(Protocol::ChangeNotification::Modify, item, collection, Collection(), resource, changedParts);
+ itemNotification(Protocol::ItemChangeNotification::Modify, item, collection, Collection(), resource, changedParts);
}
void NotificationCollector::itemsFlagsChanged(const PimItem::List &items,
@@ -74,7 +74,7 @@ void NotificationCollector::itemsFlagsChanged(const PimItem::List &items,
const Collection &collection,
const QByteArray &resource)
{
- itemNotification(Protocol::ChangeNotification::ModifyFlags, items, collection, Collection(), resource, QSet<QByteArray>(), addedFlags, removedFlags);
+ itemNotification(Protocol::ItemChangeNotification::ModifyFlags, items, collection, Collection(), resource, QSet<QByteArray>(), addedFlags, removedFlags);
}
void NotificationCollector::itemsTagsChanged(const PimItem::List &items,
@@ -83,7 +83,7 @@ void NotificationCollector::itemsTagsChanged(const PimItem::List &items,
const Collection &collection,
const QByteArray &resource)
{
- itemNotification(Protocol::ChangeNotification::ModifyTags, items, collection, Collection(), resource, QSet<QByteArray>(), QSet<QByteArray>(), QSet<QByteArray>(), addedTags, removedTags);
+ itemNotification(Protocol::ItemChangeNotification::ModifyTags, items, collection, Collection(), resource, QSet<QByteArray>(), QSet<QByteArray>(), QSet<QByteArray>(), addedTags, removedTags);
}
void NotificationCollector::itemsRelationsChanged(const PimItem::List &items,
@@ -92,7 +92,7 @@ void NotificationCollector::itemsRelationsChanged(const PimItem::List &items,
const Collection &collection,
const QByteArray &resource)
{
- itemNotification(Protocol::ChangeNotification::ModifyRelations, items, collection, Collection(), resource, QSet<QByteArray>(), QSet<QByteArray>(), QSet<QByteArray>(), QSet<qint64>(), QSet<qint64>(), addedRelations, removedRelations);
+ itemNotification(Protocol::ItemChangeNotification::ModifyRelations, items, collection, Collection(), resource, QSet<QByteArray>(), QSet<QByteArray>(), QSet<QByteArray>(), QSet<qint64>(), QSet<qint64>(), addedRelations, removedRelations);
}
void NotificationCollector::itemsMoved(const PimItem::List &items,
@@ -101,24 +101,24 @@ void NotificationCollector::itemsMoved(const PimItem::List &items,
const QByteArray &sourceResource)
{
SearchManager::instance()->scheduleSearchUpdate();
- itemNotification(Protocol::ChangeNotification::Move, items, collectionSrc, collectionDest, sourceResource);
+ itemNotification(Protocol::ItemChangeNotification::Move, items, collectionSrc, collectionDest, sourceResource);
}
void NotificationCollector::itemsRemoved(const PimItem::List &items,
const Collection &collection,
const QByteArray &resource)
{
- itemNotification(Protocol::ChangeNotification::Remove, items, collection, Collection(), resource);
+ itemNotification(Protocol::ItemChangeNotification::Remove, items, collection, Collection(), resource);
}
void NotificationCollector::itemsLinked(const PimItem::List &items, const Collection &collection)
{
- itemNotification(Protocol::ChangeNotification::Link, items, collection, Collection(), QByteArray());
+ itemNotification(Protocol::ItemChangeNotification::Link, items, collection, Collection(), QByteArray());
}
void NotificationCollector::itemsUnlinked(const PimItem::List &items, const Collection &collection)
{
- itemNotification(Protocol::ChangeNotification::Unlink, items, collection, Collection(), QByteArray());
+ itemNotification(Protocol::ItemChangeNotification::Unlink, items, collection, Collection(), QByteArray());
}
void NotificationCollector::collectionAdded(const Collection &collection,
@@ -130,7 +130,7 @@ void NotificationCollector::collectionAdded(const Collection &collection,
if (AkonadiServer::instance()->intervalChecker()) {
AkonadiServer::instance()->intervalChecker()->collectionAdded(collection.id());
}
- collectionNotification(Protocol::ChangeNotification::Add, collection, collection.parentId(), -1, resource);
+ collectionNotification(Protocol::CollectionChangeNotification::Add, collection, collection.parentId(), -1, resource);
}
void NotificationCollector::collectionChanged(const Collection &collection,
@@ -144,7 +144,7 @@ void NotificationCollector::collectionChanged(const Collection &collection,
AkonadiServer::instance()->intervalChecker()->collectionChanged(collection.id());
}
CollectionStatistics::self()->invalidateCollection(collection);
- collectionNotification(Protocol::ChangeNotification::Modify, collection, collection.parentId(), -1, resource, changes.toSet());
+ collectionNotification(Protocol::CollectionChangeNotification::Modify, collection, collection.parentId(), -1, resource, changes.toSet());
}
void NotificationCollector::collectionMoved(const Collection &collection,
@@ -158,7 +158,7 @@ void NotificationCollector::collectionMoved(const Collection &collection,
if (AkonadiServer::instance()->intervalChecker()) {
AkonadiServer::instance()->intervalChecker()->collectionChanged(collection.id());
}
- collectionNotification(Protocol::ChangeNotification::Move, collection, source.id(), collection.parentId(), resource, QSet<QByteArray>(), destResource);
+ collectionNotification(Protocol::CollectionChangeNotification::Move, collection, source.id(), collection.parentId(), resource, QSet<QByteArray>(), destResource);
}
void NotificationCollector::collectionRemoved(const Collection &collection,
@@ -171,7 +171,7 @@ void NotificationCollector::collectionRemoved(const Collection &collection,
AkonadiServer::instance()->intervalChecker()->collectionRemoved(collection.id());
}
CollectionStatistics::self()->invalidateCollection(collection);
- collectionNotification(Protocol::ChangeNotification::Remove, collection, collection.parentId(), -1, resource);
+ collectionNotification(Protocol::CollectionChangeNotification::Remove, collection, collection.parentId(), -1, resource);
}
void NotificationCollector::collectionSubscribed(const Collection &collection,
@@ -183,7 +183,7 @@ void NotificationCollector::collectionSubscribed(const Collection &collection,
if (AkonadiServer::instance()->intervalChecker()) {
AkonadiServer::instance()->intervalChecker()->collectionAdded(collection.id());
}
- collectionNotification(Protocol::ChangeNotification::Subscribe, collection, collection.parentId(), -1, resource, QSet<QByteArray>());
+ collectionNotification(Protocol::CollectionChangeNotification::Subscribe, collection, collection.parentId(), -1, resource, QSet<QByteArray>());
}
void NotificationCollector::collectionUnsubscribed(const Collection &collection,
@@ -196,32 +196,32 @@ void NotificationCollector::collectionUnsubscribed(const Collection &collection,
AkonadiServer::instance()->intervalChecker()->collectionRemoved(collection.id());
}
CollectionStatistics::self()->invalidateCollection(collection);
- collectionNotification(Protocol::ChangeNotification::Unsubscribe, collection, collection.parentId(), -1, resource, QSet<QByteArray>());
+ collectionNotification(Protocol::CollectionChangeNotification::Unsubscribe, collection, collection.parentId(), -1, resource, QSet<QByteArray>());
}
void NotificationCollector::tagAdded(const Tag &tag)
{
- tagNotification(Protocol::ChangeNotification::Add, tag);
+ tagNotification(Protocol::TagChangeNotification::Add, tag);
}
void NotificationCollector::tagChanged(const Tag &tag)
{
- tagNotification(Protocol::ChangeNotification::Modify, tag);
+ tagNotification(Protocol::TagChangeNotification::Modify, tag);
}
void NotificationCollector::tagRemoved(const Tag &tag, const QByteArray &resource, const QString &remoteId)
{
- tagNotification(Protocol::ChangeNotification::Remove, tag, resource, remoteId);
+ tagNotification(Protocol::TagChangeNotification::Remove, tag, resource, remoteId);
}
void NotificationCollector::relationAdded(const Relation &relation)
{
- relationNotification(Protocol::ChangeNotification::Add, relation);
+ relationNotification(Protocol::RelationChangeNotification::Add, relation);
}
void NotificationCollector::relationRemoved(const Relation &relation)
{
- relationNotification(Protocol::ChangeNotification::Remove, relation);
+ relationNotification(Protocol::RelationChangeNotification::Remove, relation);
}
void NotificationCollector::transactionCommitted()
@@ -244,7 +244,7 @@ void NotificationCollector::setSessionId(const QByteArray &sessionId)
mSessionId = sessionId;
}
-void NotificationCollector::itemNotification(Protocol::ChangeNotification::Operation op,
+void NotificationCollector::itemNotification(Protocol::ItemChangeNotification::Operation op,
const PimItem &item,
const Collection &collection,
const Collection &collectionDest,
@@ -256,7 +256,7 @@ void NotificationCollector::itemNotification(Protocol::ChangeNotification::Opera
itemNotification(op, items, collection, collectionDest, resource, parts);
}
-void NotificationCollector::itemNotification(Protocol::ChangeNotification::Operation op,
+void NotificationCollector::itemNotification(Protocol::ItemChangeNotification::Operation op,
const PimItem::List &items,
const Collection &collection,
const Collection &collectionDest,
@@ -272,33 +272,36 @@ void NotificationCollector::itemNotification(Protocol::ChangeNotification::Opera
Collection notificationDestCollection;
QMap<Entity::Id, QList<PimItem> > vCollections;
- if ((op == Protocol::ChangeNotification::Modify) ||
- (op == Protocol::ChangeNotification::ModifyFlags) ||
- (op == Protocol::ChangeNotification::ModifyTags) ||
- (op == Protocol::ChangeNotification::ModifyRelations)) {
+ if ((op == Protocol::ItemChangeNotification::Modify) ||
+ (op == Protocol::ItemChangeNotification::ModifyFlags) ||
+ (op == Protocol::ItemChangeNotification::ModifyTags) ||
+ (op == Protocol::ItemChangeNotification::ModifyRelations)) {
vCollections = DataStore::self()->virtualCollections(items);
}
- Protocol::ChangeNotification msg;
+ Protocol::ItemChangeNotification msg;
msg.setSessionId(mSessionId);
- msg.setType(Protocol::ChangeNotification::Items);
msg.setOperation(op);
msg.setItemParts(parts);
- QSet<QByteArray> addedFlagsAndRelations(addedFlags);
- addedFlagsAndRelations.reserve(addedRelations.size());
- Q_FOREACH (const Relation &rel, addedRelations) {
- addedFlagsAndRelations << "RELATION " + rel.relationType().name().toLatin1() + " " + QByteArray::number(rel.leftId()) + " " + QByteArray::number(rel.rightId());
- }
- msg.setAddedFlags(addedFlagsAndRelations);
- QSet<QByteArray> removedFlagsAndRelations(removedFlags);
- removedFlagsAndRelations.reserve(removedRelations.size());
- Q_FOREACH (const Relation &rel, removedRelations) {
- removedFlagsAndRelations << "RELATION " + rel.relationType().name().toLatin1() + " " + QByteArray::number(rel.leftId()) + " " + QByteArray::number(rel.rightId());
- }
- msg.setRemovedFlags(removedFlagsAndRelations);
+ msg.setAddedFlags(addedFlags);
+ msg.setRemovedFlags(removedFlags);
msg.setAddedTags(addedTags);
msg.setRemovedTags(removedTags);
+ if (!addedRelations.isEmpty()) {
+ QSet<Protocol::ItemChangeNotification::Relation> rels;
+ Q_FOREACH (const Relation &rel, addedRelations) {
+ rels.insert(Protocol::ItemChangeNotification::Relation(rel.leftId(), rel.rightId(), rel.relationType().name()));
+ }
+ msg.setAddedRelations(rels);
+ }
+ if (!removedRelations.isEmpty()) {
+ QSet<Protocol::ItemChangeNotification::Relation> rels;
+ Q_FOREACH (const Relation &rel, removedRelations) {
+ rels.insert(Protocol::ItemChangeNotification::Relation(rel.leftId(), rel.rightId(), rel.relationType().name()));
+ }
+ msg.setRemovedRelations(rels);
+ }
if (collectionDest.isValid()) {
QByteArray destResourceName;
@@ -311,10 +314,10 @@ void NotificationCollector::itemNotification(Protocol::ChangeNotification::Opera
/* Notify all virtual collections the items are linked to. */
auto iter = vCollections.constBegin(), endIter = vCollections.constEnd();
for (; iter != endIter; ++iter) {
- Protocol::ChangeNotification copy = msg;
+ Protocol::ItemChangeNotification copy = msg;
Q_FOREACH (const PimItem &item, iter.value()) {
- copy.addEntity(item.id(), item.remoteId(), item.remoteRevision(), item.mimeType().name());
+ copy.addItem(item.id(), item.remoteId(), item.remoteRevision(), item.mimeType().name());
}
copy.setParentCollection(iter.key());
copy.setResource(resource);
@@ -324,7 +327,7 @@ void NotificationCollector::itemNotification(Protocol::ChangeNotification::Opera
}
Q_FOREACH (const PimItem &item, items) {
- msg.addEntity(item.id(), item.remoteId(), item.remoteRevision(), item.mimeType().name());
+ msg.addItem(item.id(), item.remoteId(), item.remoteRevision(), item.mimeType().name());
}
Collection col;
@@ -346,7 +349,7 @@ void NotificationCollector::itemNotification(Protocol::ChangeNotification::Opera
dispatchNotification(msg);
}
-void NotificationCollector::collectionNotification(Protocol::ChangeNotification::Operation op,
+void NotificationCollector::collectionNotification(Protocol::CollectionChangeNotification::Operation op,
const Collection &collection,
Collection::Id source,
Collection::Id destination,
@@ -354,19 +357,20 @@ void NotificationCollector::collectionNotification(Protocol::ChangeNotification:
const QSet<QByteArray> &changes,
const QByteArray &destResource)
{
- Protocol::ChangeNotification msg;
- msg.setType(Protocol::ChangeNotification::Collections);
+ Protocol::CollectionChangeNotification msg;
msg.setOperation(op);
msg.setSessionId(mSessionId);
- msg.addEntity(collection.id(), collection.remoteId(), collection.remoteRevision());
+ msg.setId(collection.id());
+ msg.setRemoteId(collection.remoteId());
+ msg.setRemoteRevision(collection.remoteRevision());
msg.setParentCollection(source);
msg.setParentDestCollection(destination);
msg.setDestinationResource(destResource);
- msg.setItemParts(changes);
+ msg.setChangedParts(changes);
- if (!collection.enabled()) {
- msg.addMetadata("DISABLED");
- }
+ if (!collection.enabled()) {
+ msg.addMetadata("DISABLED");
+ }
QByteArray res = resource;
if (res.isEmpty()) {
@@ -377,36 +381,32 @@ void NotificationCollector::collectionNotification(Protocol::ChangeNotification:
dispatchNotification(msg);
}
-void NotificationCollector::tagNotification(Protocol::ChangeNotification::Operation op,
+void NotificationCollector::tagNotification(Protocol::TagChangeNotification::Operation op,
const Tag &tag,
const QByteArray &resource,
const QString &remoteId
)
{
- Protocol::ChangeNotification msg;
- msg.setType(Protocol::ChangeNotification::Tags);
+ Protocol::TagChangeNotification msg;
msg.setOperation(op);
msg.setSessionId(mSessionId);
- msg.setResource( resource );
- msg.addEntity(tag.id(), remoteId);
+ msg.setResource(resource);
+ msg.setId(tag.id());
+ msg.setRemoteId(remoteId);
dispatchNotification(msg);
}
-void NotificationCollector::relationNotification(Protocol::ChangeNotification::Operation op,
+void NotificationCollector::relationNotification(Protocol::RelationChangeNotification::Operation op,
const Relation &relation)
{
- Protocol::ChangeNotification msg;
- msg.setType(Protocol::ChangeNotification::Relations);
+ Protocol::RelationChangeNotification msg;
msg.setOperation(op);
msg.setSessionId(mSessionId);
- QSet<QByteArray> itemParts;
- //We're not using entites becaues they are not sorted
- itemParts << "LEFT " + QByteArray::number(relation.leftId());
- itemParts << "RIGHT " + QByteArray::number(relation.rightId());
- itemParts << "RID " + relation.remoteId().toLatin1();
- itemParts << "TYPE " + relation.relationType().name().toLatin1();
- msg.setItemParts(itemParts);
+ msg.setLeftItem(relation.leftId());
+ msg.setRightItem(relation.rightId());
+ msg.setRemoteId(relation.remoteId());
+ msg.setType(relation.relationType().name());
dispatchNotification(msg);
}
@@ -414,9 +414,13 @@ void NotificationCollector::relationNotification(Protocol::ChangeNotification::O
void NotificationCollector::dispatchNotification(const Protocol::ChangeNotification &msg)
{
if (!mDb || mDb->inTransaction()) {
- Protocol::ChangeNotification::appendAndCompress(mNotifications, msg);
+ if (msg.type() == Protocol::Command::CollectionChangeNotification) {
+ Protocol::CollectionChangeNotification::appendAndCompress(mNotifications, msg);
+ } else {
+ mNotifications.append(msg);
+ }
} else {
- Q_EMIT notify(Protocol::ChangeNotification::List() << msg);
+ Q_EMIT notify({ msg });
}
}
diff --git a/src/server/storage/notificationcollector.h b/src/server/storage/notificationcollector.h
index a6e4ab4..536f1a6 100644
--- a/src/server/storage/notificationcollector.h
+++ b/src/server/storage/notificationcollector.h
@@ -220,7 +220,8 @@ Q_SIGNALS:
void notify(const Akonadi::Protocol::ChangeNotification::List &msgs);
private:
- void itemNotification(Protocol::ChangeNotification::Operation op, const PimItem::List &items,
+ void itemNotification(Protocol::ItemChangeNotification::Operation op,
+ const PimItem::List &items,
const Collection &collection,
const Collection &collectionDest,
const QByteArray &resource,
@@ -231,23 +232,24 @@ private:
const QSet<qint64> &removedTags = QSet<qint64>(),
const Relation::List &addedRelations = Relation::List(),
const Relation::List &removedRelations = Relation::List() );
- void itemNotification(Protocol::ChangeNotification::Operation op, const PimItem &item,
+ void itemNotification(Protocol::ItemChangeNotification::Operation op,
+ const PimItem &item,
const Collection &collection,
const Collection &collectionDest,
const QByteArray &resource,
const QSet<QByteArray> &parts = QSet<QByteArray>());
- void collectionNotification(Protocol::ChangeNotification::Operation op,
+ void collectionNotification(Protocol::CollectionChangeNotification::Operation op,
const Collection &collection,
Collection::Id source, Collection::Id destination,
const QByteArray &resource,
const QSet<QByteArray> &changes = QSet<QByteArray>(),
const QByteArray &destResource = QByteArray());
- void tagNotification(Protocol::ChangeNotification::Operation op,
+ void tagNotification(Protocol::TagChangeNotification::Operation op,
const Tag &tag,
const QByteArray &resource = QByteArray(),
const QString &remoteId = QString());
- void relationNotification(Protocol::ChangeNotification::Operation op,
- const Relation &relation);
+ void relationNotification(Protocol::RelationChangeNotification::Operation op,
+ const Relation &relation);
void dispatchNotification(const Protocol::ChangeNotification &msg);
void clear();
diff --git a/src/shared/aktest.h b/src/shared/aktest.h
index f3e193b..41ce49c 100644
--- a/src/shared/aktest.h
+++ b/src/shared/aktest.h
@@ -71,7 +71,7 @@ inline void akTestSetInstanceIdentifier(const QString &instanceId)
namespace QTest {
template<>
-char *toString(const Akonadi::Protocol::ChangeNotification &msg)
+char *toString(const Akonadi::Protocol::ItemChangeNotification &msg)
{
return qstrdup(qPrintable(msg.debugString()));
}
@@ -100,13 +100,10 @@ enum NtfField {
};
typedef QFlags<NtfField> NtfFields;
-bool compareNotifications(const Akonadi::Protocol::ChangeNotification &actual,
- const Akonadi::Protocol::ChangeNotification &expected,
+bool compareNotifications(const Akonadi::Protocol::ItemChangeNotification &actual,
+ const Akonadi::Protocol::ItemChangeNotification &expected,
const NtfFields fields = NtfAll)
{
- if (fields & NtfType) {
- AKCOMPARE(actual.type(), expected.type());
- }
if (fields & NtfOperation) {
AKCOMPARE(actual.operation(), expected.operation());
}
@@ -114,7 +111,7 @@ bool compareNotifications(const Akonadi::Protocol::ChangeNotification &actual,
AKCOMPARE(actual.sessionId(), expected.sessionId());
}
if (fields & NtfEntities) {
- AKCOMPARE(actual.entities(), expected.entities());
+ AKCOMPARE(actual.items(), expected.items());
}
if (fields & NtfResource) {
AKCOMPARE(actual.resource(), expected.resource());