diff --git a/0031-Don-t-leak-external-payload-files.patch b/0031-Don-t-leak-external-payload-files.patch deleted file mode 100644 index e62fea7..0000000 --- a/0031-Don-t-leak-external-payload-files.patch +++ /dev/null @@ -1,134 +0,0 @@ -commit 9c0dc6b3f0826d32eac310b2e7ecd858ca3df681 -Author: Dan Vrátil -Date: Mon Jun 29 22:45:11 2015 +0200 - - Don't leak old external payload files - - Actually delete old payload files after we increase the payload revision or - switch from external to internal payload. This caused ~/.local/share/akonadi/file_db_data - to grow insanely for all users, leaving them with many duplicated files (just with - different revisions). - - It is recommended that users run akonadictl fsck to clean up the leaked payload - files. - - Note that there won't be any more releases of Akonadi 1.13 (and this has been - fixed in master already), so I strongly recommend distributions to pick this - patch into their packaging. - - BUG: 341884 - CCBUG: 338402 - -diff --git a/server/src/storage/partstreamer.cpp b/server/src/storage/partstreamer.cpp -index 2ec41fa..71bdca8 100644 ---- a/server/src/storage/partstreamer.cpp -+++ b/server/src/storage/partstreamer.cpp -@@ -290,6 +290,12 @@ bool PartStreamer::stream(const QByteArray &command, bool checkExists, - mDataChanged = true; - } - -+ // If the part is external, remember it's current file name -+ QString originalFile; -+ if (part.isValid() && part.external()) { -+ originalFile = PartHelper::resolveAbsolutePath(part.data()); -+ } -+ - part.setPartType(partType); - part.setVersion(partVersion); - part.setPimItemId(mItem.id()); -@@ -306,6 +312,14 @@ bool PartStreamer::stream(const QByteArray &command, bool checkExists, - *changed = mDataChanged; - } - -+ if (!originalFile.isEmpty()) { -+ // If the part was external but is not anymore, or if it's still external -+ // but the filename has changed (revision update), remove the original file -+ if (!part.external() || (part.external() && originalFile != PartHelper::resolveAbsolutePath(part.data()))) { -+ PartHelper::removeFile(originalFile); -+ } -+ } -+ - return ok; - } - -diff --git a/server/tests/unittest/partstreamertest.cpp b/server/tests/unittest/partstreamertest.cpp -index 05e3a8a..669bbbc 100644 ---- a/server/tests/unittest/partstreamertest.cpp -+++ b/server/tests/unittest/partstreamertest.cpp -@@ -91,6 +91,7 @@ private Q_SLOTS: - QTest::addColumn("expectedPartSize"); - QTest::addColumn("expectedChanged"); - QTest::addColumn("isExternal"); -+ QTest::addColumn("version"); - QTest::addColumn("pimItem"); - - PimItem item; -@@ -101,22 +102,22 @@ private Q_SLOTS: - QVERIFY(item.insert()); - - // Order of these tests matters! -- QTest::newRow("item 1, internal") << QByteArray("PLD:DATA") << QByteArray("123") << 3ll << true << false << item; -- QTest::newRow("item 1, change to external") << QByteArray("PLD:DATA") << QByteArray("123456789") << 9ll << true << true << item; -- QTest::newRow("item 1, update external") << QByteArray("PLD:DATA") << QByteArray("987654321") << 9ll << true << true << item; -- QTest::newRow("item 1, external, no change") << QByteArray("PLD:DATA") << QByteArray("987654321") << 9ll << false << true << item; -- QTest::newRow("item 1, change to internal") << QByteArray("PLD:DATA") << QByteArray("1234") << 4ll << true << false << item; -- QTest::newRow("item 1, internal, no change") << QByteArray("PLD:DATA") << QByteArray("1234") << 4ll << false << false << item; -+ QTest::newRow("item 1, internal") << QByteArray("PLD:DATA") << QByteArray("123") << 3ll << true << false << -1 << item; -+ QTest::newRow("item 1, change to external") << QByteArray("PLD:DATA") << QByteArray("123456789") << 9ll << true << true << 0 << item; -+ QTest::newRow("item 1, update external") << QByteArray("PLD:DATA") << QByteArray("987654321") << 9ll << true << true << 1 << item; -+ QTest::newRow("item 1, external, no change") << QByteArray("PLD:DATA") << QByteArray("987654321") << 9ll << false << true << 2 << item; -+ QTest::newRow("item 1, change to internal") << QByteArray("PLD:DATA") << QByteArray("1234") << 4ll << true << false << 2 << item; -+ QTest::newRow("item 1, internal, no change") << QByteArray("PLD:DATA") << QByteArray("1234") << 4ll << false << false << 2 << item; - } - - void testStreamer() - { -- return; - QFETCH(QByteArray, expectedPartName); - QFETCH(QByteArray, expectedData); - QFETCH(qint64, expectedPartSize); - QFETCH(bool, expectedChanged); - QFETCH(bool, isExternal); -+ QFETCH(int, version); - QFETCH(PimItem, pimItem); - - FakeConnection connection; -@@ -160,17 +161,18 @@ private Q_SLOTS: - - PimItem item = PimItem::retrieveById(pimItem.id()); - const QVector parts = item.parts(); -- QVERIFY(parts.count() == 1); -+ QCOMPARE(parts.count(), 1); - const Part part = parts[0]; - QCOMPARE(part.datasize(), expectedPartSize); - QCOMPARE(part.external(), isExternal); -+ qDebug() << part.version() << part.data(); - const QByteArray data = part.data(); - if (isExternal) { - QVERIFY(streamerSpy.count() == 1); - QVERIFY(streamerSpy.first().count() == 1); - const Response response = streamerSpy.first().first().value(); - const QByteArray str = response.asString(); -- const QByteArray expectedResponse = "+ STREAM [FILE " + QByteArray::number(part.id()) + "_r" + QByteArray::number(part.version()) + "]"; -+ const QByteArray expectedResponse = "+ STREAM [FILE " + QByteArray::number(part.id()) + "_r" + QByteArray::number(version) + "]"; - QCOMPARE(QString::fromUtf8(str), QString::fromUtf8(expectedResponse)); - - QFile file(PartHelper::resolveAbsolutePath(data)); -@@ -182,7 +184,7 @@ private Q_SLOTS: - QCOMPARE(fileData, expectedData); - - // Make sure no previous versions are left behind in file_db_data -- for (int i = 0; i < part.version(); ++i) { -+ for (int i = 0; i < version; ++i) { - const QByteArray fileName = QByteArray::number(part.id()) + "_r" + QByteArray::number(part.version()); - const QString filePath = PartHelper::resolveAbsolutePath(fileName); - QVERIFY(!QFile::exists(filePath)); -@@ -194,7 +196,7 @@ private Q_SLOTS: - QCOMPARE(data, expectedData); - - // Make sure nothing is left behind in file_db_data -- for (int i = 0; i <= part.version(); ++i) { -+ for (int i = 0; i <= version; ++i) { - const QByteArray fileName = QByteArray::number(part.id()) + "_r" + QByteArray::number(part.version()); - const QString filePath = PartHelper::resolveAbsolutePath(fileName); - QVERIFY(!QFile::exists(filePath)); diff --git a/0031-Less-C-11-fixes-build-with-clang.patch b/0031-Less-C-11-fixes-build-with-clang.patch new file mode 100644 index 0000000..f915ce2 --- /dev/null +++ b/0031-Less-C-11-fixes-build-with-clang.patch @@ -0,0 +1,52 @@ +From c23607679fa1451f0c6890bd4a5656c07d519853 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +Date: Fri, 6 Feb 2015 13:33:50 +0100 +Subject: [PATCH 31/34] Less C++11, fixes build with clang + +--- + server/tests/unittest/searchtest.cpp | 28 ++++++++++++++-------------- + 1 file changed, 14 insertions(+), 14 deletions(-) + +diff --git a/server/tests/unittest/searchtest.cpp b/server/tests/unittest/searchtest.cpp +index f523b09..969d296 100644 +--- a/server/tests/unittest/searchtest.cpp ++++ b/server/tests/unittest/searchtest.cpp +@@ -119,21 +119,21 @@ private Q_SLOTS: + QTest::addColumn("mimetypes"); + QTest::addColumn>("expectedResults"); + +- QTest::newRow("") << QVector({ 0 }) +- << QStringList({ QLatin1String("text/plain") }) +- << QVector({ col4.id(), col5.id(), col7.id() }); +- QTest::newRow("") << QVector({ 0 }) +- << QStringList({ QLatin1String("application/octet-stream") }) +- << QVector({ col2.id(), col3.id(), col6.id(), col8.id() }); +- QTest::newRow("") << QVector({ col1.id() }) +- << QStringList({ QLatin1String("text/plain") }) +- << QVector({ col4.id() }); +- QTest::newRow("") << QVector({ col1.id() }) +- << QStringList({ QLatin1String("unique/mime-type") }) ++ QTest::newRow("") << (QVector() << 0) ++ << (QStringList() << QLatin1String("text/plain")) ++ << (QVector() << col4.id() << col5.id() << col7.id()); ++ QTest::newRow("") << (QVector() << 0) ++ << (QStringList() << QLatin1String("application/octet-stream")) ++ << (QVector() << col2.id() << col3.id() << col6.id() << col8.id()); ++ QTest::newRow("") << (QVector() << col1.id()) ++ << (QStringList() << QLatin1String("text/plain")) ++ << (QVector() << col4.id()); ++ QTest::newRow("") << (QVector() << col1.id()) ++ << (QStringList() << QLatin1String("unique/mime-type")) + << QVector(); +- QTest::newRow("") << QVector({ col2.id(), col7.id() }) +- << QStringList({ QLatin1String("application/octet-stream") }) +- << QVector({ col3.id(), col8.id() }); ++ QTest::newRow("") << (QVector() << col2.id() << col7.id()) ++ << (QStringList() << QLatin1String("application/octet-stream")) ++ << (QVector() << col3.id() << col8.id()); + } + + void testSearchHelperCollectionListing() +-- +2.4.3 + diff --git a/0032-Don-t-throw-exception-when-MOVE-handler-finds-no-ite.patch b/0032-Don-t-throw-exception-when-MOVE-handler-finds-no-ite.patch new file mode 100644 index 0000000..62a7637 --- /dev/null +++ b/0032-Don-t-throw-exception-when-MOVE-handler-finds-no-ite.patch @@ -0,0 +1,60 @@ +From abe71f46c3b2e657db25ac16c43a4c76b2212a9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +Date: Wed, 17 Jun 2015 13:04:13 +0200 +Subject: [PATCH 32/34] Don't throw exception when MOVE handler finds no items + to move + +Instead return "OK MOVE complete" right away. The reason for this is that +when client tries to move an Item from a folder into the same folder (it's +possible in KMail, also mailfilter agent might trigger this situation) the +subsequent command gets eaten by ImapStreamParser and the client's Job gets +stuck waiting for response forever. According to Laurent this could also fix +the Mail Filter Agent getting stuck occasionally. + +The problem is in ImapStreamParser::atCommandEnd() method, which is called +by the Move handler at some point. atCommandEnd() checks whether we reached +command end in the stream by looking if the next characters in the stream +are "\r\n" and if so it will consume the command end ("\r\n"), effectively +moving the streaming position BEYOND the command. In case of MOVE the +command has already been completely parsed so we are actually at the end of +the command and so ImapStreamParser will consume the "\r\n" and position the +stream beyond the command end. + +After that the Move handler tries to get the items from DB and throws the +exception (the second part of the condition in the SQL query causes that +the query yields no results in this situation) which gets us back to +Connection where we then call ImapStreamParser::skipCommand(). At this point +however there are no more data in the stream (because atCommandEnd() moved +us beyond the end of the MOVE command) and so ImapStreamParser will block +and wait for more data (with 30 seconds timeout). If client sends another +command within this time the ImapStreamParser will think that this is the +command to be skipped and will consume it. This means that the command never +really reaches the Connection as it's consumed as soon as it's captured by +ImapStreamParser. And because Akonadi never receives the command it cannot +send a response and thus the Job in client will wait forever and ever... + +Proper fix would be to make ImapStreamParser::atCommandEnd() to only peek +instead of actually altering the position in the stream however I'm really +afraid that it could break some other stuff that relies on this (broken?) +behaviour and our test coverage is not sufficient at this point to be +reliable enough. +--- + server/src/handler/move.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/src/handler/move.cpp b/server/src/handler/move.cpp +index 0a6c3bf..4cf9d4e 100644 +--- a/server/src/handler/move.cpp ++++ b/server/src/handler/move.cpp +@@ -85,7 +85,7 @@ bool Move::parseStream() + if ( qb.exec() ) { + const QVector items = qb.result(); + if ( items.isEmpty() ) { +- throw HandlerException( "No items found" ); ++ return successResponse( "MOVE complete" ); + } + + // Split the list by source collection +-- +2.4.3 + diff --git a/0033-Don-t-leak-old-external-payload-files.patch b/0033-Don-t-leak-old-external-payload-files.patch new file mode 100644 index 0000000..2325de0 --- /dev/null +++ b/0033-Don-t-leak-old-external-payload-files.patch @@ -0,0 +1,140 @@ +From 9c0dc6b3f0826d32eac310b2e7ecd858ca3df681 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +Date: Mon, 29 Jun 2015 22:45:11 +0200 +Subject: [PATCH 33/34] Don't leak old external payload files + +Actually delete old payload files after we increase the payload revision or +switch from external to internal payload. This caused ~/.local/share/akonadi/file_db_data +to grow insanely for all users, leaving them with many duplicated files (just with +different revisions). + +It is recommended that users run akonadictl fsck to clean up the leaked payload +files. + +Note that there won't be any more releases of Akonadi 1.13 (and this has been +fixed in master already), so I strongly recommend distributions to pick this +patch into their packaging. + +BUG: 341884 +CCBUG: 338402 +--- + server/src/storage/partstreamer.cpp | 14 ++++++++++++++ + server/tests/unittest/partstreamertest.cpp | 24 +++++++++++++----------- + 2 files changed, 27 insertions(+), 11 deletions(-) + +diff --git a/server/src/storage/partstreamer.cpp b/server/src/storage/partstreamer.cpp +index 2ec41fa..71bdca8 100644 +--- a/server/src/storage/partstreamer.cpp ++++ b/server/src/storage/partstreamer.cpp +@@ -290,6 +290,12 @@ bool PartStreamer::stream(const QByteArray &command, bool checkExists, + mDataChanged = true; + } + ++ // If the part is external, remember it's current file name ++ QString originalFile; ++ if (part.isValid() && part.external()) { ++ originalFile = PartHelper::resolveAbsolutePath(part.data()); ++ } ++ + part.setPartType(partType); + part.setVersion(partVersion); + part.setPimItemId(mItem.id()); +@@ -306,6 +312,14 @@ bool PartStreamer::stream(const QByteArray &command, bool checkExists, + *changed = mDataChanged; + } + ++ if (!originalFile.isEmpty()) { ++ // If the part was external but is not anymore, or if it's still external ++ // but the filename has changed (revision update), remove the original file ++ if (!part.external() || (part.external() && originalFile != PartHelper::resolveAbsolutePath(part.data()))) { ++ PartHelper::removeFile(originalFile); ++ } ++ } ++ + return ok; + } + +diff --git a/server/tests/unittest/partstreamertest.cpp b/server/tests/unittest/partstreamertest.cpp +index 05e3a8a..669bbbc 100644 +--- a/server/tests/unittest/partstreamertest.cpp ++++ b/server/tests/unittest/partstreamertest.cpp +@@ -91,6 +91,7 @@ private Q_SLOTS: + QTest::addColumn("expectedPartSize"); + QTest::addColumn("expectedChanged"); + QTest::addColumn("isExternal"); ++ QTest::addColumn("version"); + QTest::addColumn("pimItem"); + + PimItem item; +@@ -101,22 +102,22 @@ private Q_SLOTS: + QVERIFY(item.insert()); + + // Order of these tests matters! +- QTest::newRow("item 1, internal") << QByteArray("PLD:DATA") << QByteArray("123") << 3ll << true << false << item; +- QTest::newRow("item 1, change to external") << QByteArray("PLD:DATA") << QByteArray("123456789") << 9ll << true << true << item; +- QTest::newRow("item 1, update external") << QByteArray("PLD:DATA") << QByteArray("987654321") << 9ll << true << true << item; +- QTest::newRow("item 1, external, no change") << QByteArray("PLD:DATA") << QByteArray("987654321") << 9ll << false << true << item; +- QTest::newRow("item 1, change to internal") << QByteArray("PLD:DATA") << QByteArray("1234") << 4ll << true << false << item; +- QTest::newRow("item 1, internal, no change") << QByteArray("PLD:DATA") << QByteArray("1234") << 4ll << false << false << item; ++ QTest::newRow("item 1, internal") << QByteArray("PLD:DATA") << QByteArray("123") << 3ll << true << false << -1 << item; ++ QTest::newRow("item 1, change to external") << QByteArray("PLD:DATA") << QByteArray("123456789") << 9ll << true << true << 0 << item; ++ QTest::newRow("item 1, update external") << QByteArray("PLD:DATA") << QByteArray("987654321") << 9ll << true << true << 1 << item; ++ QTest::newRow("item 1, external, no change") << QByteArray("PLD:DATA") << QByteArray("987654321") << 9ll << false << true << 2 << item; ++ QTest::newRow("item 1, change to internal") << QByteArray("PLD:DATA") << QByteArray("1234") << 4ll << true << false << 2 << item; ++ QTest::newRow("item 1, internal, no change") << QByteArray("PLD:DATA") << QByteArray("1234") << 4ll << false << false << 2 << item; + } + + void testStreamer() + { +- return; + QFETCH(QByteArray, expectedPartName); + QFETCH(QByteArray, expectedData); + QFETCH(qint64, expectedPartSize); + QFETCH(bool, expectedChanged); + QFETCH(bool, isExternal); ++ QFETCH(int, version); + QFETCH(PimItem, pimItem); + + FakeConnection connection; +@@ -160,17 +161,18 @@ private Q_SLOTS: + + PimItem item = PimItem::retrieveById(pimItem.id()); + const QVector parts = item.parts(); +- QVERIFY(parts.count() == 1); ++ QCOMPARE(parts.count(), 1); + const Part part = parts[0]; + QCOMPARE(part.datasize(), expectedPartSize); + QCOMPARE(part.external(), isExternal); ++ qDebug() << part.version() << part.data(); + const QByteArray data = part.data(); + if (isExternal) { + QVERIFY(streamerSpy.count() == 1); + QVERIFY(streamerSpy.first().count() == 1); + const Response response = streamerSpy.first().first().value(); + const QByteArray str = response.asString(); +- const QByteArray expectedResponse = "+ STREAM [FILE " + QByteArray::number(part.id()) + "_r" + QByteArray::number(part.version()) + "]"; ++ const QByteArray expectedResponse = "+ STREAM [FILE " + QByteArray::number(part.id()) + "_r" + QByteArray::number(version) + "]"; + QCOMPARE(QString::fromUtf8(str), QString::fromUtf8(expectedResponse)); + + QFile file(PartHelper::resolveAbsolutePath(data)); +@@ -182,7 +184,7 @@ private Q_SLOTS: + QCOMPARE(fileData, expectedData); + + // Make sure no previous versions are left behind in file_db_data +- for (int i = 0; i < part.version(); ++i) { ++ for (int i = 0; i < version; ++i) { + const QByteArray fileName = QByteArray::number(part.id()) + "_r" + QByteArray::number(part.version()); + const QString filePath = PartHelper::resolveAbsolutePath(fileName); + QVERIFY(!QFile::exists(filePath)); +@@ -194,7 +196,7 @@ private Q_SLOTS: + QCOMPARE(data, expectedData); + + // Make sure nothing is left behind in file_db_data +- for (int i = 0; i <= part.version(); ++i) { ++ for (int i = 0; i <= version; ++i) { + const QByteArray fileName = QByteArray::number(part.id()) + "_r" + QByteArray::number(part.version()); + const QString filePath = PartHelper::resolveAbsolutePath(fileName); + QVERIFY(!QFile::exists(filePath)); +-- +2.4.3 + diff --git a/0034-set-cmake_min_req-to-match-kdelibs4-and-enable-newer.patch b/0034-set-cmake_min_req-to-match-kdelibs4-and-enable-newer.patch new file mode 100644 index 0000000..9d6411f --- /dev/null +++ b/0034-set-cmake_min_req-to-match-kdelibs4-and-enable-newer.patch @@ -0,0 +1,25 @@ +From 18ed37d89b8185ac15a8bfe245de8a88d17f2c64 Mon Sep 17 00:00:00 2001 +From: David Faure +Date: Tue, 28 Jul 2015 12:47:44 +0200 +Subject: [PATCH 34/34] set cmake_min_req to match kdelibs4 and enable newer + policies + +--- + CMakeLists.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 2d790c9..a64e724 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1,6 +1,6 @@ ++cmake_minimum_required(VERSION 2.8.9) + project(Akonadi) + +-cmake_minimum_required(VERSION 2.8.8 FATAL_ERROR) + + # where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked + set(CMAKE_MODULE_PATH "${Akonadi_SOURCE_DIR}/cmake/modules") +-- +2.4.3 + diff --git a/akonadi.spec b/akonadi.spec index cc275ac..11760eb 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 18%{?dist} +Release: 19%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -37,7 +37,7 @@ Source10: akonadiserverrc.mysql ## upstreamable patches -## upstream patches +## upstream patches (1.13 branch) Patch1: 0001-FindSqlite-Use-CMAKE_FLAGS-the-right-way-in-try_comp.patch Patch2: 0002-Do-not-enter-the-test-directories-if-AKONADI_BUILD_T.patch Patch3: 0003-STORE-Allow-modifying-items-tags-via-Tag-RID-or-GID.patch @@ -68,7 +68,10 @@ Patch27: 0027-Minor-improvements-in-StatisticsCache-as-suggested-b.patch Patch28: 0028-Extend-imapparser-benchmark-and-keep-static-data-aro.patch Patch29: 0029-Reduce-the-amount-of-allocations-by-preallocating-a-.patch Patch30: 0030-Preallocate-a-capacity-of-16-for-the-returned-list.patch -Patch31: 0031-Don-t-leak-external-payload-files.patch +Patch31: 0031-Less-C-11-fixes-build-with-clang.patch +Patch32: 0032-Don-t-throw-exception-when-MOVE-handler-finds-no-ite.patch +Patch33: 0033-Don-t-leak-old-external-payload-files.patch +Patch34: 0034-set-cmake_min_req-to-match-kdelibs4-and-enable-newer.patch %define mysql_conf_timestamp 20140709 @@ -235,6 +238,9 @@ fi %changelog +* Fri Jul 31 2015 Rex Dieter 1.13.0-19 +- pull in latest 1.13 branch fixes + * Wed Jul 29 2015 Fedora Release Engineering - 1.13.0-18 - Rebuilt for https://fedoraproject.org/wiki/Changes/F23Boost159