Fixed version number to make version smaller than the version in sid: akonadi/4:18.08.3-7~deb10u1
hefee
diff -Nru akonadi-18.08.3/debian/changelog akonadi-18.08.3/debian/changelog --- akonadi-18.08.3/debian/changelog 2019-04-29 16:24:10.000000000 +0200 +++ akonadi-18.08.3/debian/changelog 2019-08-30 22:11:22.000000000 +0200 @@ -1,3 +1,34 @@ +akonadi (4:18.08.3-7~deb10u1) buster; urgency=medium + + * Rebuild for buster. + + -- Sandro Knauß <he...@debian.org> Fri, 30 Aug 2019 22:11:22 +0200 + +akonadi (4:18.08.3-7) unstable; urgency=medium + + * Team upload. + + [ Sandro Knauß ] + * Add patch to fix: Akonadi components crash on logout. (Closes: #939013) + * Add patch to fix: Automatic recovery from Multiple Merge Candidates + error (Closes: #939012) + * Add patch with files, that are needed for other patches. + * Update symbols from buildds for 4:18.08.3 + + -- Sandro Knauß <he...@debian.org> Fri, 30 Aug 2019 12:59:47 +0200 + +akonadi (4:18.08.3-6) unstable; urgency=medium + + * Team upload. + + [ Sandro Knauß ] + * Fix "Akonadi don't anwser any requests and ends in deadlock" (Closes: #935981) + by adding upstream patches. + - Akonadi-fix-dangling-transaction-after-itemsync-fail.patch + - ItemSync-skip-handling-remote-items-if-local-changes.patch + + -- Sandro Knauß <he...@debian.org> Wed, 28 Aug 2019 19:31:31 +0200 + akonadi (4:18.08.3-5) unstable; urgency=medium * Team upload. diff -Nru akonadi-18.08.3/debian/libkf5akonadiagentbase5.symbols akonadi-18.08.3/debian/libkf5akonadiagentbase5.symbols --- akonadi-18.08.3/debian/libkf5akonadiagentbase5.symbols 2019-02-08 23:19:51.000000000 +0100 +++ akonadi-18.08.3/debian/libkf5akonadiagentbase5.symbols 2019-08-30 12:58:59.000000000 +0200 @@ -1,4 +1,4 @@ -# SymbolsHelper-Confirmed: 4:18.07.90 amd64 +# SymbolsHelper-Confirmed: 4:18.08.3 alpha amd64 arm64 armel armhf hppa hurd-i386 i386 m68k mips64el mipsel ppc64 ppc64el riscv64 s390x x32 libKF5AkonadiAgentBase.so.5 libkf5akonadiagentbase5 #MINVER# * Build-Depends-Package: libkf5akonadi-dev _ZN7Akonadi12ResourceBase10cancelTaskERK7QString@Base 4:15.07.90 @@ -197,7 +197,6 @@ _ZNK7Akonadi9AgentBase8isOnlineEv@Base 4:15.07.90 _ZNK7Akonadi9AgentBase8progressEv@Base 4:15.07.90 _ZNK7Akonadi9AgentBase9agentNameEv@Base 4:15.07.90 - (optional=templinst)_ZSt4swapIN7Akonadi10CollectionEENSt9enable_ifIXsrSt6__and_IJSt6__not_ISt15__is_tuple_likeIT_EESt21is_move_constructibleIS6_ESt18is_move_assignableIS6_EEE5valueEvE4typeERS6_SG_@Base 4:18.07.90 _ZTI12QDBusContext@Base 4:15.07.90 _ZTIN7Akonadi12ResourceBaseE@Base 4:15.07.90 _ZTIN7Akonadi16PreprocessorBaseE@Base 4:15.07.90 diff -Nru akonadi-18.08.3/debian/libkf5akonadicore5abi2.symbols akonadi-18.08.3/debian/libkf5akonadicore5abi2.symbols --- akonadi-18.08.3/debian/libkf5akonadicore5abi2.symbols 2019-02-13 19:42:05.000000000 +0100 +++ akonadi-18.08.3/debian/libkf5akonadicore5abi2.symbols 2019-08-30 12:58:21.000000000 +0200 @@ -1,4 +1,4 @@ -# SymbolsHelper-Confirmed: 4:18.08.3 alpha amd64 arm64 armel armhf hppa hurd-i386 i386 mips mips64el mipsel powerpc ppc64 ppc64el s390x +# SymbolsHelper-Confirmed: 4:18.08.3 alpha amd64 arm64 armel armhf hppa hurd-i386 i386 m68k mips mips64el mipsel powerpc ppc64 ppc64el riscv64 s390x x32 libKF5AkonadiCore.so.5abi2 libkf5akonadicore5abi2 #MINVER# * Build-Depends-Package: libkf5akonadi-dev ABI_5_2@ABI_5_2 4:18.07.90 @@ -27,7 +27,8 @@ _ZN7Akonadi10Collection7fromUrlERK4QUrl@ABI_5_2 4:18.07.90 _ZN7Akonadi10Collection7setNameERK7QString@ABI_5_2 4:18.07.90 _ZN7Akonadi10Collection8mimeTypeEv@ABI_5_2 4:18.07.90 - (optional=templinst|arch=hurd-i386 i386 m68k)_ZN7Akonadi10Collection9attributeINS_25PersistentSearchAttributeEEEPT_NS0_12CreateOptionE@ABI_5_2 4:18.07.90 + (optional=templinst|arch=!mips !powerpc)_ZN7Akonadi10Collection9attributeINS_22EntityDisplayAttributeEEEPT_NS0_12CreateOptionE@ABI_5_2 4:18.08.3 + (optional=templinst|arch=alpha amd64 arm64 armel armhf hppa hurd-i386 i386 m68k mips64el mipsel ppc64 ppc64el riscv64 s390x x32)_ZN7Akonadi10Collection9attributeINS_25PersistentSearchAttributeEEEPT_NS0_12CreateOptionE@ABI_5_2 4:18.07.90 _ZN7Akonadi10Collection9setRightsE6QFlagsINS0_5RightEE@ABI_5_2 4:18.07.90 _ZN7Akonadi10CollectionC1ERKS0_@ABI_5_2 4:18.07.90 _ZN7Akonadi10CollectionC1Ev@ABI_5_2 4:18.07.90 @@ -1597,7 +1598,7 @@ _ZN7Akonadi9UnlinkJobD2Ev@ABI_5_2 4:18.07.90 _ZN9QHashData9hasShrunkEv@ABI_5_2 4:18.07.90 (optional=templinst)_ZNK12KConfigGroup9readEntryIxEE5QListIT_EPKcRKS3_@ABI_5_2 4:18.07.90 - (optional=templinst|arch=alpha hppa mips64el ppc64 ppc64el s390x)_ZNK12KConfigGroup9readEntryIxEET_PKcRKS1_@ABI_5_2 4:18.07.90 + (optional=templinst|arch=alpha hppa mips64el ppc64 ppc64el riscv64 s390x)_ZNK12KConfigGroup9readEntryIxEET_PKcRKS1_@ABI_5_2 4:18.07.90 _ZNK7Akonadi10Collection10attributesEv@ABI_5_2 4:18.07.90 _ZNK7Akonadi10Collection10referencedEv@ABI_5_2 4:18.07.90 _ZNK7Akonadi10Collection10shouldListENS0_11ListPurposeE@ABI_5_2 4:18.07.90 @@ -1620,7 +1621,9 @@ _ZNK7Akonadi10Collection8remoteIdEv@ABI_5_2 4:18.07.90 _ZNK7Akonadi10Collection8resourceEv@ABI_5_2 4:18.07.90 _ZNK7Akonadi10Collection9attributeERK10QByteArray@ABI_5_2 4:18.07.90 + (optional=templinst|arch=!mips !powerpc)_ZNK7Akonadi10Collection9attributeINS_22EntityDeletedAttributeEEEPT_v@ABI_5_2 4:18.08.3 (optional=templinst)_ZNK7Akonadi10Collection9attributeINS_22EntityDisplayAttributeEEEPT_v@ABI_5_2 4:18.07.90 + (optional=templinst|arch=!mips !powerpc)_ZNK7Akonadi10Collection9attributeINS_24CollectionQuotaAttributeEEEPT_v@ABI_5_2 4:18.08.3 _ZNK7Akonadi10Collection9isVirtualEv@ABI_5_2 4:18.07.90 _ZNK7Akonadi10CollectioneqERKS0_@ABI_5_2 4:18.07.90 _ZNK7Akonadi10CollectionltERKS0_@ABI_5_2 4:18.07.90 @@ -2055,7 +2058,7 @@ (optional=templinst)_ZNK7Akonadi4Item11payloadImplINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEENSt9enable_ifIXntsrNS_8Internal12PayloadTraitIT_EE13isPolymorphicESB_E4typeEv@ABI_5_2 4:18.07.90 _ZNK7Akonadi4Item11payloadPathEv@ABI_5_2 4:18.07.90 _ZNK7Akonadi4Item12hasAttributeERK10QByteArray@ABI_5_2 4:18.07.90 - (optional=templinst|arch=armel armhf mips mipsel powerpc)_ZNK7Akonadi4Item12hasAttributeINS_22EntityDisplayAttributeEEEbv@ABI_5_2 4:18.07.90 + (optional=templinst|arch=mips powerpc)_ZNK7Akonadi4Item12hasAttributeINS_22EntityDisplayAttributeEEEbv@ABI_5_2 4:18.07.90 _ZNK7Akonadi4Item13payloadBaseV2Eii@ABI_5_2 4:18.07.90 _ZNK7Akonadi4Item14remoteRevisionEv@ABI_5_2 4:18.07.90 _ZNK7Akonadi4Item16ensureMetaTypeIdEi@ABI_5_2 4:18.07.90 @@ -2153,8 +2156,8 @@ _ZNK7Akonadi9ItemModel8rowCountERK11QModelIndex@ABI_5_2 4:18.07.90 _ZNK7Akonadi9ItemModel9mimeTypesEv@ABI_5_2 4:18.07.90 _ZNK7Akonadi9UnlinkJob10metaObjectEv@ABI_5_2 4:18.07.90 - (optional=templinst|arch=armel)_ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE1EE10_M_releaseEv@ABI_5_2 4:18.07.90 - (optional=templinst|arch=!armel)_ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE10_M_releaseEv@ABI_5_2 4:18.07.90 + (optional=templinst|arch=armel riscv64)_ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE1EE10_M_releaseEv@ABI_5_2 4:18.07.90 + (optional=templinst|arch=!armel !riscv64)_ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE10_M_releaseEv@ABI_5_2 4:18.07.90 (optional=templinst)_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPcEEvT_S7_St20forward_iterator_tag@ABI_5_2 4:18.07.90 _ZTIN5boost16exception_detail10clone_baseE@ABI_5_2 4:18.07.90 _ZTIN5boost9exceptionE@ABI_5_2 4:18.07.90 @@ -2259,11 +2262,11 @@ _ZTIN7Akonadi9ExceptionE@ABI_5_2 4:18.07.90 _ZTIN7Akonadi9ItemModelE@ABI_5_2 4:18.07.90 _ZTIN7Akonadi9UnlinkJobE@ABI_5_2 4:18.07.90 - (arch=armel)_ZTIN9__gnu_cxx7__mutexE@ABI_5_2 4:18.07.90 - (arch=armel)_ZTISt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE1EE@ABI_5_2 4:18.07.90 - (arch=!armel)_ZTISt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE2EE@ABI_5_2 4:18.07.90 - (arch=armel)_ZTISt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE1EE@ABI_5_2 4:18.07.90 - (arch=!armel)_ZTISt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE@ABI_5_2 4:18.07.90 + (arch=armel riscv64)_ZTIN9__gnu_cxx7__mutexE@ABI_5_2 4:18.08.3 + (arch=armel riscv64)_ZTISt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE1EE@ABI_5_2 4:18.08.3 + (arch=!armel !riscv64)_ZTISt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE2EE@ABI_5_2 4:18.07.90 + (arch=armel riscv64)_ZTISt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE1EE@ABI_5_2 4:18.08.3 + (arch=!armel !riscv64)_ZTISt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE@ABI_5_2 4:18.07.90 _ZTSN5boost16exception_detail10clone_baseE@ABI_5_2 4:18.07.90 _ZTSN5boost9exceptionE@ABI_5_2 4:18.07.90 _ZTSN7Akonadi10ConnectionE@ABI_5_2 4:18.07.90 @@ -2367,11 +2370,11 @@ _ZTSN7Akonadi9ExceptionE@ABI_5_2 4:18.07.90 _ZTSN7Akonadi9ItemModelE@ABI_5_2 4:18.07.90 _ZTSN7Akonadi9UnlinkJobE@ABI_5_2 4:18.07.90 - (arch=armel)_ZTSN9__gnu_cxx7__mutexE@ABI_5_2 4:18.07.90 - (arch=armel)_ZTSSt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE1EE@ABI_5_2 4:18.07.90 - (arch=!armel)_ZTSSt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE2EE@ABI_5_2 4:18.07.90 - (arch=armel)_ZTSSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE1EE@ABI_5_2 4:18.07.90 - (arch=!armel)_ZTSSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE@ABI_5_2 4:18.07.90 + (arch=armel riscv64)_ZTSN9__gnu_cxx7__mutexE@ABI_5_2 4:18.08.3 + (arch=armel riscv64)_ZTSSt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE1EE@ABI_5_2 4:18.08.3 + (arch=!armel !riscv64)_ZTSSt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE2EE@ABI_5_2 4:18.07.90 + (arch=armel riscv64)_ZTSSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE1EE@ABI_5_2 4:18.08.3 + (arch=!armel !riscv64)_ZTSSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE@ABI_5_2 4:18.07.90 _ZTVN5boost16exception_detail10clone_baseE@ABI_5_2 4:18.07.90 _ZTVN5boost9exceptionE@ABI_5_2 4:18.07.90 _ZTVN7Akonadi10ConnectionE@ABI_5_2 4:18.07.90 diff -Nru akonadi-18.08.3/debian/libkf5akonadiwidgets5abi1.symbols akonadi-18.08.3/debian/libkf5akonadiwidgets5abi1.symbols --- akonadi-18.08.3/debian/libkf5akonadiwidgets5abi1.symbols 2019-02-13 19:42:06.000000000 +0100 +++ akonadi-18.08.3/debian/libkf5akonadiwidgets5abi1.symbols 2019-08-30 12:58:21.000000000 +0200 @@ -1,4 +1,4 @@ -# SymbolsHelper-Confirmed: 4:18.08.3 alpha amd64 arm64 armel armhf hppa hurd-i386 i386 mips mips64el mipsel powerpc ppc64 ppc64el s390x +# SymbolsHelper-Confirmed: 4:18.08.3 alpha amd64 arm64 armel armhf hppa hurd-i386 i386 m68k mips mips64el mipsel powerpc ppc64 ppc64el riscv64 s390x x32 libKF5AkonadiWidgets.so.5abi1 libkf5akonadiwidgets5abi1 #MINVER# * Build-Depends-Package: libkf5akonadi-dev ABI_5_1@ABI_5_1 4:17.12.1 @@ -403,7 +403,8 @@ _ZN7Akonadi9TagWidgetD1Ev@ABI_5_1 4:17.12.1 _ZN7Akonadi9TagWidgetD2Ev@ABI_5_1 4:17.12.1 (optional=templinst)_ZNK12KConfigGroup9readEntryI5QSizeEET_PKcRKS2_@ABI_5_1 4:17.12.1 - (optional=templinst|arch=!amd64 !arm64)_ZNK12KConfigGroup9readEntryIbEET_PKcRKS1_@ABI_5_1 4:18.08.3 + (optional=templinst|arch=!amd64 !arm64 !x32)_ZNK12KConfigGroup9readEntryIbEET_PKcRKS1_@ABI_5_1 4:18.08.3 + (optional=templinst|arch=!mips !powerpc)_ZNK7Akonadi10Collection9attributeINS_22EntityDisplayAttributeEEEPT_v@ABI_5_1 4:18.08.3 _ZNK7Akonadi10ControlGui10metaObjectEv@ABI_5_1 4:17.12.1 _ZNK7Akonadi13TagEditWidget10metaObjectEv@ABI_5_1 4:17.12.1 _ZNK7Akonadi13TagEditWidget9selectionEv@ABI_5_1 4:17.12.1 diff -Nru akonadi-18.08.3/debian/patches/Akonadi-fix-dangling-transaction-after-itemsync-fail.patch akonadi-18.08.3/debian/patches/Akonadi-fix-dangling-transaction-after-itemsync-fail.patch --- akonadi-18.08.3/debian/patches/Akonadi-fix-dangling-transaction-after-itemsync-fail.patch 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-18.08.3/debian/patches/Akonadi-fix-dangling-transaction-after-itemsync-fail.patch 2019-08-28 18:39:24.000000000 +0200 @@ -0,0 +1,70 @@ +From ca67354dcc5b4640f26de0b3e46c79cf1e50bc32 Mon Sep 17 00:00:00 2001 +From: David Faure <fa...@kde.org> +Date: Sun, 3 Mar 2019 11:10:36 +0100 +Subject: [PATCH 1/2] Akonadi: fix dangling transaction after itemsync failure + +Summary: +TransactionSequence was emitting result() twice when rolling back. + +* How did this happen? +The TransactionRollbackJob is (automatically) added as a subjob of the +TransactionSequence, so when it finishes, slotResult is called (like for +all subjobs), as well as rollbackResult(). +Since the latter emits result() already [mostly for symmetry with +commitResult()], we don't need to do that in slotResult (which doesn't do +it for the case of committing, either). + +* Why is it a problem to emit result() twice? +Well, first, it's against the law in KJob world. In practice, +ItemSyncPrivate::slotTransactionResult was called twice (for the same +TransactionSequence job) which made it decrement mTransactionJobs one +time too many. +As a result, checkDone() finished too early and didn't go into the +"commit transaction" branch for other transactions. +Leaving a transaction "open" is a good recipe for database deadlocks further +down the line. + +* Why did the TransactionSequence roll back in the first place? +In my case because of the infamous and not-yet fixed "Multiple merge +candidates" problem, but it seems that it can also happen when having +items without a part, according to Volker's investigations. +All of these issues still need to be fixed, but at least akonadi seems +to be still usable after they happen. + +CCBUG: 399167 + +Test Plan: Ctrl+L in kmail, with a folder having multiple items for the same RID + +Reviewers: dvratil, vkrause + +Reviewed By: dvratil + +Subscribers: kfunk, kde-pim + +Tags: #kde_pim + +Differential Revision: https://phabricator.kde.org/D19487 + +(cherry picked from commit f1281cf18f40fd69acd61c31b48f5ce43e138eea) +(cherry picked from commit 8ff596c4fe15199b66262c624d8b7c8d8ec7368f) +(cherry picked from commit 15c91a0ac93051465b37807efceb6e9fd36cb73b) +--- + src/core/jobs/transactionsequence.cpp | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/src/core/jobs/transactionsequence.cpp b/src/core/jobs/transactionsequence.cpp +index a7b50f075..89b956d5f 100644 +--- a/src/core/jobs/transactionsequence.cpp ++++ b/src/core/jobs/transactionsequence.cpp +@@ -133,8 +133,6 @@ void TransactionSequence::slotResult(KJob *job) + d->mState = TransactionSequencePrivate::Committing; + TransactionCommitJob *job = new TransactionCommitJob(this); + connect(job, &TransactionCommitJob::result, [d](KJob *job) { d->commitResult(job);}); +- } else if (d->mState == TransactionSequencePrivate::RollingBack) { +- emitResult(); + } + } + } else { +-- +2.23.0 + diff -Nru akonadi-18.08.3/debian/patches/Automatic-recovery-from-Multiple-Merge-Candidates-er.patch akonadi-18.08.3/debian/patches/Automatic-recovery-from-Multiple-Merge-Candidates-er.patch --- akonadi-18.08.3/debian/patches/Automatic-recovery-from-Multiple-Merge-Candidates-er.patch 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-18.08.3/debian/patches/Automatic-recovery-from-Multiple-Merge-Candidates-er.patch 2019-08-30 12:20:10.000000000 +0200 @@ -0,0 +1,285 @@ +From 8332cf8a5aa39df6fb665cdbff1a48286d5698f5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Daniel=20Vr=C3=A1til?= <dvra...@kde.org> +Date: Fri, 31 May 2019 13:20:27 +0200 +Subject: [PATCH 1/2] Automatic recovery from Multiple Merge Candidates error + +Summary: +Introduce a recovery codepath when Multiple Merge Candidates error +occurs during Item merging. Since clients generally do not use +merging, this really only happens during ItemSync. In such case we +quitely delete all the conflicting items from the database and reschedule +the collection sync. The next sync should then succeed and bring the +collection into a consistent state. + +Note that this does not fix the Multiple Merge Candidates bug - it can +still happen (and we still don't know how), but Akonadi should now be +able to recover from it automatically without user intervention, thus +making this issue less of a PITA. + +CCBUG: 338658 + +Test Plan: Successfuly auto-recovered a broken collection on my setup. + +Reviewers: #kde_pim, dfaure + +Reviewed By: dfaure + +Subscribers: vkrause, dfaure, ngraham, asturmlechner, kde-pim + +Tags: #kde_pim + +Differential Revision: https://phabricator.kde.org/D21455 + +(cherry picked from commit 8f230d7d7f8a4e2b97273585374a68902b5ef6cf) +--- + autotests/server/fakedatastore.cpp | 6 +-- + autotests/server/fakedatastore.h | 2 +- + src/server/handler/akappend.cpp | 72 +++++++++++++++++++++++++++--- + src/server/handler/akappend.h | 4 ++ + src/server/storage/datastore.cpp | 34 +++++++------- + src/server/storage/datastore.h | 4 +- + 6 files changed, 96 insertions(+), 26 deletions(-) + +diff --git a/autotests/server/fakedatastore.cpp b/autotests/server/fakedatastore.cpp +index d384e423c..7f6101b82 100644 +--- a/autotests/server/fakedatastore.cpp ++++ b/autotests/server/fakedatastore.cpp +@@ -259,11 +259,11 @@ bool FakeDataStore::appendPimItem(QVector<Part> &parts, + remote_id, remoteRevision, gid, pimItem); + } + +-bool FakeDataStore::cleanupPimItems(const PimItem::List &items) ++bool FakeDataStore::cleanupPimItems(const PimItem::List &items, bool silent) + { + mChanges.insert(QStringLiteral("cleanupPimItems"), +- QVariantList() << QVariant::fromValue(items)); +- return DataStore::cleanupPimItems(items); ++ QVariantList() << QVariant::fromValue(items) << silent); ++ return DataStore::cleanupPimItems(items, silent); + } + + bool FakeDataStore::unhidePimItem(PimItem &pimItem) +diff --git a/autotests/server/fakedatastore.h b/autotests/server/fakedatastore.h +index 01f7714b4..be1b5efcf 100644 +--- a/autotests/server/fakedatastore.h ++++ b/autotests/server/fakedatastore.h +@@ -104,7 +104,7 @@ public: + const QString &gid, + PimItem &pimItem) override; + +- virtual bool cleanupPimItems(const PimItem::List &items) override; ++ virtual bool cleanupPimItems(const PimItem::List &items, bool silent = false) override; + + virtual bool unhidePimItem(PimItem &pimItem) override; + virtual bool unhideAllPimItems() override; +diff --git a/src/server/handler/akappend.cpp b/src/server/handler/akappend.cpp +index 7c3afef3d..4d47c8171 100644 +--- a/src/server/handler/akappend.cpp ++++ b/src/server/handler/akappend.cpp +@@ -30,8 +30,12 @@ + #include "storage/partstreamer.h" + #include "storage/parthelper.h" + #include "storage/selectquerybuilder.h" ++#include "storage/itemretrievalmanager.h" + #include <private/externalpartstorage_p.h> + ++#include "shared/akranges.h" ++#include "shared/akscopeguard.h" ++ + #include <numeric> //std::accumulate + + using namespace Akonadi; +@@ -354,6 +358,61 @@ bool AkAppend::notify(const PimItem &item, const Collection &collection, + return true; + } + ++void AkAppend::recoverFromMultipleMergeCandidates(const PimItem::List &items, const Collection &collection) ++{ ++ // HACK HACK HACK: When this happens within ItemSync, we are running inside a client-side ++ // transaction, so just calling commit here won't have any effect, since this handler will ++ // ultimately fail and the client will rollback the transaction. To circumvent this, we ++ // will forcibly commit the transaction, do our changes here within a new transaction and ++ // then we open a new transaction so that the client won't notice. ++ ++ DataStore *db = DataStore::self(); ++ int transactionDepth = 0; ++ while (db->inTransaction()) { ++ ++transactionDepth; ++ db->commitTransaction(); ++ } ++ const AkScopeGuard restoreTransaction([&]() { ++ for (int i = 0; i < transactionDepth; ++i) { ++ db->beginTransaction(QStringLiteral("RestoredTransactionAfterMMCRecovery")); ++ } ++ }); ++ ++ Transaction transaction(db, QStringLiteral("MMC Recovery Transaction")); ++ ++ // If any of the conflicting items is dirty or does not have a remote ID, we don't want to remove ++ // them as it would cause data loss. There's a chance next changeReplay will fix this, so ++ // next time the ItemSync hits this multiple merge candidates, all changes will be committed ++ // and this check will succeed ++ if (items | any([](const auto &item) { return item.dirty() || item.remoteId().isEmpty(); })) { ++ qCWarning(AKONADISERVER_LOG) << "Automatic multiple merge candidates recovery failed: at least one of the candidates has uncommitted changes!"; ++ return; ++ } ++ ++ // This cannot happen with ItemSync, but in theory could happen during individual GID merge. ++ if (items | any([collection](const auto &item) { return item.collectionId() != collection.id(); })) { ++ qCWarning(AKONADISERVER_LOG) << "Automatic multiple merge candidates recovery failed: all candidates do not belong to the same collection."; ++ return; ++ } ++ ++ db->cleanupPimItems(items, DataStore::Silent); ++ if (!transaction.commit()) { ++ qCWarning(AKONADISERVER_LOG) << "Automatic multiple merge candidates recovery failed: failed to commit database transaction."; ++ return; ++ } ++ ++ ++ // Schedule a new sync of the collection, one that will succeed ++ const auto resource = collection.resource().name(); ++ QMetaObject::invokeMethod(ItemRetrievalManager::instance(), "triggerCollectionSync", ++ Qt::QueuedConnection, ++ Q_ARG(QString, resource), Q_ARG(qint64, collection.id())); ++ ++ qCInfo(AKONADISERVER_LOG) << "Automatic multiple merge candidates recovery successful: conflicting items" << (items | transform([](const auto &i) { return i.id(); }) | toQVector) ++ << "in collection" << collection.name() << "(ID:" << collection.id() << ") were removed and a new sync was scheduled in the resource" ++ << resource; ++} ++ + bool AkAppend::parseStream() + { + const auto &cmd = Protocol::cmdCast<Protocol::CreateItemCommand>(m_command); +@@ -372,7 +431,6 @@ bool AkAppend::parseStream() + return false; + } + +- + if (cmd.mergeModes() == Protocol::CreateItemCommand::None) { + if (!insertItem(cmd, item, parentCol)) { + return false; +@@ -436,16 +494,20 @@ bool AkAppend::parseStream() + } + storageTrx.commit(); + } else { +- qCDebug(AKONADISERVER_LOG) << "Multiple merge candidates:"; ++ qCWarning(AKONADISERVER_LOG) << "Multiple merge candidates, will attempt to recover:"; + for (const PimItem &item : result) { + qCDebug(AKONADISERVER_LOG) << "\tID:" << item.id() << ", RID:" << item.remoteId() + << ", GID:" << item.gid() + << ", Collection:" << item.collection().name() << "(" << item.collectionId() << ")" + << ", Resource:" << item.collection().resource().name() << "(" << item.collection().resourceId() << ")"; + } +- // Nor GID or RID are guaranteed to be unique, so make sure we don't merge +- // something we don't want +- return failureResponse(QStringLiteral("Multiple merge candidates, aborting")); ++ ++ transaction.commit(); // commit the current transaction, before we attempt MMC recovery ++ recoverFromMultipleMergeCandidates(result, parentCol); ++ ++ // Even if the recovery was successful, indicate error to force the client to abort the ++ // sync, since we've interfered with the overall state. ++ return failureResponse(QStringLiteral("Multiple merge candidates in collection '%1', aborting").arg(item.collection().name())); + } + } + +diff --git a/src/server/handler/akappend.h b/src/server/handler/akappend.h +index 98c9bc206..99de77907 100644 +--- a/src/server/handler/akappend.h ++++ b/src/server/handler/akappend.h +@@ -27,6 +27,8 @@ namespace Akonadi + namespace Server + { + ++class Transaction; ++ + /** + @ingroup akonadi_server_handler + +@@ -61,6 +63,8 @@ private: + bool notify(const PimItem &item, bool seen, const Collection &collection); + bool notify(const PimItem &item, const Collection &collection, + const QSet<QByteArray> &changedParts); ++ ++ void recoverFromMultipleMergeCandidates(const PimItem::List &items, const Collection &collection); + }; + + } // namespace Server +diff --git a/src/server/storage/datastore.cpp b/src/server/storage/datastore.cpp +index f846af5b1..ff1330ee5 100644 +--- a/src/server/storage/datastore.cpp ++++ b/src/server/storage/datastore.cpp +@@ -1158,29 +1158,31 @@ bool DataStore::unhideAllPimItems() + return false; + } + +-bool DataStore::cleanupPimItems(const PimItem::List &items) ++bool DataStore::cleanupPimItems(const PimItem::List &items, bool silent) + { + // generate relation removed notifications +- for (const PimItem &item : items) { +- SelectQueryBuilder<Relation> relationQuery; +- relationQuery.addValueCondition(Relation::leftIdFullColumnName(), Query::Equals, item.id()); +- relationQuery.addValueCondition(Relation::rightIdFullColumnName(), Query::Equals, item.id()); +- relationQuery.setSubQueryMode(Query::Or); ++ if (!silent) { ++ for (const PimItem &item : items) { ++ SelectQueryBuilder<Relation> relationQuery; ++ relationQuery.addValueCondition(Relation::leftIdFullColumnName(), Query::Equals, item.id()); ++ relationQuery.addValueCondition(Relation::rightIdFullColumnName(), Query::Equals, item.id()); ++ relationQuery.setSubQueryMode(Query::Or); + +- if (!relationQuery.exec()) { +- throw HandlerException("Failed to obtain relations"); +- } +- const Relation::List relations = relationQuery.result(); +- for (const Relation &relation : relations) { +- notificationCollector()->relationRemoved(relation); ++ if (!relationQuery.exec()) { ++ throw HandlerException("Failed to obtain relations"); ++ } ++ const Relation::List relations = relationQuery.result(); ++ for (const Relation &relation : relations) { ++ notificationCollector()->relationRemoved(relation); ++ } + } +- } + +- // generate the notification before actually removing the data +- notificationCollector()->itemsRemoved(items); ++ // generate the notification before actually removing the data ++ notificationCollector()->itemsRemoved(items); ++ } + + // FIXME: Create a single query to do this +- Q_FOREACH (const PimItem &item, items) { ++ for (const auto &item : items) { + if (!item.clearFlags()) { + return false; + } +diff --git a/src/server/storage/datastore.h b/src/server/storage/datastore.h +index 2f6fb36df..1eb934fd7 100644 +--- a/src/server/storage/datastore.h ++++ b/src/server/storage/datastore.h +@@ -103,6 +103,8 @@ class DataStore : public QObject + { + Q_OBJECT + public: ++ const constexpr static bool Silent = true; ++ + /** + Closes the database connection and destroys the DataStore object. + */ +@@ -199,7 +201,7 @@ public: + /** + * Removes the pim item and all referenced data ( e.g. flags ) + */ +- virtual bool cleanupPimItems(const PimItem::List &items); ++ virtual bool cleanupPimItems(const PimItem::List &items, bool silent = false); + + /** + * Unhides the specified PimItem. Emits the itemAdded() notification as +-- +2.23.0 + diff -Nru akonadi-18.08.3/debian/patches/Backport-missing-files-from-master.patch akonadi-18.08.3/debian/patches/Backport-missing-files-from-master.patch --- akonadi-18.08.3/debian/patches/Backport-missing-files-from-master.patch 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-18.08.3/debian/patches/Backport-missing-files-from-master.patch 2019-08-30 12:26:27.000000000 +0200 @@ -0,0 +1,894 @@ +From 80f8e85cee83afd5af5b9694aef0b7fbaffb68a2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Sandro=20Knau=C3=9F?= <skna...@kde.org> +Date: Fri, 30 Aug 2019 10:13:59 +0200 +Subject: [PATCH] Backport missing files from master. + +src/shared/akhelpers.h d6f65c5733f35de3061e9c73c54eb3bf53ffe04b +src/shared/akranges.h 354139b155cc9295c97fcd7887738fedce1b6837 +src/shared/akscopeguard.h 179bd0e766b314d976951e00a9fc4d4d6a8938a2 +src/shared/aktraits.h f85c06a7b3253882d8a1fba626fde07291df6892 +--- + src/shared/akhelpers.h | 119 +++++++++ + src/shared/akranges.h | 511 ++++++++++++++++++++++++++++++++++++++ + src/shared/akscopeguard.h | 52 ++++ + src/shared/aktraits.h | 165 ++++++++++++ + 4 files changed, 847 insertions(+) + create mode 100644 src/shared/akhelpers.h + create mode 100644 src/shared/akranges.h + create mode 100644 src/shared/akscopeguard.h + create mode 100644 src/shared/aktraits.h + +diff --git a/src/shared/akhelpers.h b/src/shared/akhelpers.h +new file mode 100644 +index 000000000..e1ab2ba29 +--- /dev/null ++++ b/src/shared/akhelpers.h +@@ -0,0 +1,119 @@ ++/* ++ Copyright (C) 2018 - 2019 Daniel Vrátil <dvra...@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_AKHELPERS_H_ ++#define AKONADI_AKHELPERS_H_ ++ ++#include <type_traits> ++#include <utility> ++ ++namespace Akonadi ++{ ++namespace detail ++{ ++ ++// C++14-compatible implementation of std::invoke(), based on implementation ++// from https://en.cppreference.com/w/cpp/utility/functional/invoke ++ ++template<typename T> ++struct is_reference_wrapper : std::false_type {}; ++template<typename T> ++struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {}; ++template<typename T> ++constexpr bool is_reference_wrapper_v = is_reference_wrapper<T>::value; ++ ++template<typename T> ++constexpr bool is_member_function_pointer_v = std::is_member_function_pointer<std::remove_cv_t<std::remove_reference_t<T>>>::value; ++ ++template<typename T, typename Type, typename T1, typename ... Args> ++auto invoke(Type T::* fun, T1 &&t1, Args && ... args) -> ++ std::enable_if_t<is_member_function_pointer_v<decltype(fun)> ++ && std::is_base_of<T, std::decay_t<T1>>::value, ++ std::result_of_t<decltype(fun)(T, Args ...)>> ++{ ++ return (std::forward<T1>(t1).*fun)(std::forward<Args>(args) ...); ++} ++ ++template<typename T, typename Type, typename T1, typename ... Args> ++auto invoke(Type T::* fun, T1 &&t1, Args && ... args) -> ++ std::enable_if_t<is_member_function_pointer_v<decltype(fun)> ++ && is_reference_wrapper_v<std::decay_t<T1>>, ++ std::result_of_t<decltype(fun)(T, Args ...)>> ++{ ++ return (t1.get().*fun)(std::forward<Args>(args) ...); ++} ++ ++template<typename T, typename Type, typename T1, typename ... Args> ++auto invoke(Type T::* fun, T1 &&t1, Args && ... args) -> ++ std::enable_if_t<is_member_function_pointer_v<decltype(fun)> ++ && !std::is_base_of<T, std::decay_t<T1>>::value ++ && !is_reference_wrapper_v<std::decay_t<T1>>, ++ std::result_of_t<decltype(fun)(T1, Args ...)>> ++{ ++ return ((*std::forward<T1>(t1)).*fun)(std::forward<Args>(args) ...); ++} ++ ++template<typename T, typename Type, typename T1, typename ... Args> ++auto invoke(Type T::* fun, T1 &&t1, Args && ...) -> ++ std::enable_if_t<!is_member_function_pointer_v<decltype(fun)> ++ && std::is_base_of<T1, std::decay_t<T1>>::value, ++ std::result_of_t<decltype(fun)(T1, Args ...)>> ++{ ++ return std::forward<T1>(t1).*fun; ++} ++ ++template<typename T, typename Type, typename T1, typename ... Args> ++auto invoke(Type T::* fun, T1 &&t1, Args && ...) -> ++ std::enable_if_t<!is_member_function_pointer_v<decltype(fun)> ++ && is_reference_wrapper_v<std::decay_t<T1>>, ++ std::result_of_t<decltype(fun)(T1, Args ...)>> ++{ ++ return t1.get().*fun; ++} ++ ++template<typename T, typename Type, typename T1, typename ... Args> ++auto invoke(Type T::* fun, T1 &&t1, Args && ...) -> ++ std::enable_if_t<!is_member_function_pointer_v<decltype(fun)> ++ && !std::is_base_of<T1, std::decay_t<T1>>::value ++ && !is_reference_wrapper_v<std::decay_t<T1>>, ++ std::result_of_t<decltype(fun)(T1, Args ...)>> ++{ ++ return (*std::forward<T1>(t1)).*fun; ++} ++ ++template<typename Fun, typename ... Args> ++auto invoke(Fun &&fun, Args && ... args) ++{ ++ return std::forward<Fun>(fun)(std::forward<Args>(args) ...); ++} ++ ++} // namespace detail ++ ++template<typename Fun, typename ... Args> ++auto invoke(Fun &&fun, Args && ... args) ++{ ++ return detail::invoke(std::forward<Fun>(fun), std::forward<Args>(args) ...); ++} ++ ++static const auto IsNull = [](auto ptr) { return !(bool)ptr; }; ++static const auto IsNotNull = [](auto ptr) { return (bool)ptr; }; ++ ++} // namespace Akonadi ++ ++#endif +diff --git a/src/shared/akranges.h b/src/shared/akranges.h +new file mode 100644 +index 000000000..6ee22f092 +--- /dev/null ++++ b/src/shared/akranges.h +@@ -0,0 +1,511 @@ ++/* ++ Copyright (C) 2018 - 2019 Daniel Vrátil <dvra...@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_AKRANGES_H ++#define AKONADI_AKRANGES_H ++ ++#include "aktraits.h" ++#include "akhelpers.h" ++ ++#include <QVector> ++#include <QSet> ++#include <QMap> ++ ++#include <algorithm> ++#include <functional> ++#include <utility> ++#include <type_traits> ++#include <iterator> ++ ++namespace Akonadi { ++ ++namespace detail { ++ ++template<template<typename> class Cont> ++struct To_ ++{ ++ template<typename T> using Container = Cont<T>; ++}; ++ ++template<template<typename, typename> class Cont> ++struct ToAssoc_ ++{ ++ template<typename Key, typename Value> using Container = Cont<Key, Value>; ++}; ++ ++struct Values_ {}; ++struct Keys_ {}; ++ ++template<typename RangeLike, typename OutContainer, ++ AK_REQUIRES(AkTraits::isAppendable<OutContainer>)> ++OutContainer copyContainer(const RangeLike &range) ++{ ++ OutContainer rv; ++ rv.reserve(range.size()); ++ for (auto &&v : range) { ++ rv.push_back(std::move(v)); ++ } ++ return rv; ++} ++ ++template<typename RangeLike, typename OutContainer, ++ AK_REQUIRES(AkTraits::isInsertable<OutContainer>)> ++OutContainer copyContainer(const RangeLike &range) ++{ ++ OutContainer rv; ++ rv.reserve(range.size()); ++ for (const auto &v : range) { ++ rv.insert(v); ++ } ++ return rv; ++} ++ ++template<typename RangeList, typename OutContainer> ++OutContainer copyAssocContainer(const RangeList &range) ++{ ++ OutContainer rv; ++ for (const auto &v : range) { ++ rv.insert(v.first, v.second); ++ } ++ return rv; ++} ++ ++template<typename Iterator> ++struct IteratorTrait { ++ using iterator_category = typename Iterator::iterator_category; ++ using value_type = typename Iterator::value_type; ++ using difference_type = typename Iterator::difference_type; ++ using pointer = typename Iterator::pointer; ++ using reference = typename Iterator::reference; ++}; ++ ++// Without QT_STRICT_ITERATORS QVector and QList iterators do not satisfy STL ++// iterator concepts since they are nothing more but typedefs to T* - for those ++// we need to provide custom traits. ++template<typename Iterator> ++struct IteratorTrait<Iterator*> { ++ // QTypedArrayData::iterator::iterator_category ++ using iterator_category = std::random_access_iterator_tag; ++ using value_type = Iterator; ++ using difference_type = int; ++ using pointer = Iterator*; ++ using reference = Iterator&; ++}; ++ ++template<typename Iterator> ++struct IteratorTrait<const Iterator *> { ++ using iterator_category = std::random_access_iterator_tag; ++ using value_type = Iterator; ++ using difference_type = int; ++ using pointer = const Iterator *; ++ using reference = const Iterator &; ++}; ++ ++template<typename IterImpl, typename RangeLike, typename Iterator = typename RangeLike::const_iterator> ++struct IteratorBase ++{ ++public: ++ using iterator_category = typename IteratorTrait<Iterator>::iterator_category; ++ using value_type = typename IteratorTrait<Iterator>::value_type; ++ using difference_type = typename IteratorTrait<Iterator>::difference_type; ++ using pointer = typename IteratorTrait<Iterator>::pointer; ++ using reference = typename IteratorTrait<Iterator>::reference; ++ ++ IteratorBase(const IteratorBase<IterImpl, RangeLike> &other) ++ : mIter(other.mIter), mRange(other.mRange) ++ {} ++ ++ IterImpl &operator++() ++ { ++ ++static_cast<IterImpl*>(this)->mIter; ++ return *static_cast<IterImpl*>(this); ++ } ++ ++ IterImpl operator++(int) ++ { ++ auto ret = *static_cast<IterImpl*>(this); ++ ++static_cast<IterImpl*>(this)->mIter; ++ return ret; ++ } ++ ++ bool operator==(const IterImpl &other) const ++ { ++ return mIter == other.mIter; ++ } ++ ++ bool operator!=(const IterImpl &other) const ++ { ++ return !(*static_cast<const IterImpl*>(this) == other); ++ } ++ ++ bool operator<(const IterImpl &other) const ++ { ++ return mIter < other.mIter; ++ } ++ ++ auto operator-(const IterImpl &other) const ++ { ++ return mIter - other.mIter; ++ } ++ ++ auto operator*() const ++ { ++ return *mIter; ++ } ++ ++protected: ++ IteratorBase(const Iterator &iter, const RangeLike &range) ++ : mIter(iter), mRange(range) ++ {} ++ IteratorBase(const Iterator &iter, RangeLike &&range) ++ : mIter(iter), mRange(std::move(range)) ++ {} ++ ++ Iterator mIter; ++ RangeLike mRange; ++}; ++ ++template<typename RangeLike, typename TransformFn, typename Iterator = typename RangeLike::const_iterator> ++struct TransformIterator : public IteratorBase<TransformIterator<RangeLike, TransformFn>, RangeLike> ++{ ++private: ++ template<typename ... T> ++ struct ResultOf; ++ ++ template<typename R, typename ... Args> ++ struct ResultOf<R(Args ...)> { ++ using type = R; ++ }; ++ ++ template<typename ... Ts> ++ using FuncHelper = decltype(Akonadi::invoke(std::declval<Ts>() ...))(Ts ...); ++ using IteratorValueType = typename ResultOf<FuncHelper<TransformFn, typename IteratorTrait<Iterator>::value_type>>::type; ++public: ++ using value_type = IteratorValueType; ++ using pointer = IteratorValueType *; // FIXME: preserve const-ness ++ using reference = const IteratorValueType &; // FIXME: preserve const-ness ++ ++ TransformIterator(const Iterator &iter, const TransformFn &fn, const RangeLike &range) ++ : IteratorBase<TransformIterator<RangeLike, TransformFn>, RangeLike>(iter, range) ++ , mFn(fn) ++ { ++ } ++ ++ auto operator*() const ++ { ++ return Akonadi::invoke(mFn, *this->mIter); ++ } ++ ++private: ++ TransformFn mFn; ++}; ++ ++template<typename RangeLike, typename Predicate, typename Iterator = typename RangeLike::const_iterator> ++class FilterIterator : public IteratorBase<FilterIterator<RangeLike, Predicate>, RangeLike> ++{ ++public: ++ FilterIterator(const Iterator &iter, const Iterator &end, const Predicate &predicate, const RangeLike &range) ++ : IteratorBase<FilterIterator<RangeLike, Predicate>, RangeLike>(iter, range) ++ , mPredicate(predicate), mEnd(end) ++ { ++ while (this->mIter != mEnd && !Akonadi::invoke(mPredicate, *this->mIter)) { ++ ++this->mIter; ++ } ++ } ++ ++ auto &operator++() ++ { ++ if (this->mIter != mEnd) { ++ do { ++ ++this->mIter; ++ } while (this->mIter != mEnd && !Akonadi::invoke(mPredicate, *this->mIter)); ++ } ++ return *this; ++ } ++ ++ auto operator++(int) ++ { ++ auto it = *this; ++ ++(*this); ++ return it; ++ } ++ ++private: ++ Predicate mPredicate; ++ Iterator mEnd; ++}; ++ ++ ++template<typename Container, int Pos, typename Iterator = typename Container::const_key_value_iterator> ++class AssociativeContainerIterator ++ : public IteratorBase<AssociativeContainerIterator<Container, Pos>, Container, Iterator> ++{ ++public: ++ using value_type = std::remove_const_t<std::remove_reference_t< ++ typename std::tuple_element<Pos, typename Iterator::value_type>::type>>; ++ using pointer = std::add_pointer_t<value_type>; ++ using reference = std::add_lvalue_reference_t<value_type>; ++ ++ AssociativeContainerIterator(const Iterator &iter, const Container &container) ++ : IteratorBase<AssociativeContainerIterator<Container, Pos>, Container, Iterator>(iter, container) ++ {} ++ ++ auto operator*() const ++ { ++ return std::get<Pos>(*this->mIter); ++ } ++}; ++ ++template<typename Container> ++using AssociativeContainerKeyIterator = AssociativeContainerIterator<Container, 0>; ++template<typename Container> ++using AssociativeContainerValueIterator = AssociativeContainerIterator<Container, 1>; ++ ++ ++template<typename Iterator> ++struct Range ++{ ++public: ++ using iterator = Iterator; ++ using const_iterator = Iterator; ++ using value_type = typename detail::IteratorTrait<Iterator>::value_type; ++ ++ Range(Iterator &&begin, Iterator &&end) ++ : mBegin(std::move(begin)) ++ , mEnd(std::move(end)) ++ {} ++ ++ Iterator begin() const ++ { ++ return mBegin; ++ } ++ ++ Iterator cbegin() const ++ { ++ return mBegin; ++ } ++ ++ Iterator end() const ++ { ++ return mEnd; ++ } ++ ++ Iterator cend() const ++ { ++ return mEnd; ++ } ++ ++ auto size() const ++ { ++ return std::distance(mBegin, mEnd); ++ } ++ ++private: ++ Iterator mBegin; ++ Iterator mEnd; ++}; ++ ++template<typename T> ++using IsRange = typename std::is_same<T, Range<typename T::iterator>>; ++ ++template<typename TransformFn> ++struct Transform_ ++{ ++ TransformFn mFn; ++}; ++ ++template<typename PredicateFn> ++struct Filter_ ++{ ++ PredicateFn mFn; ++}; ++ ++template<typename EachFun> ++struct ForEach_ ++{ ++ EachFun mFn; ++}; ++ ++template<typename Predicate> ++struct All_ ++{ ++ Predicate mFn; ++}; ++ ++template<typename Predicate> ++struct Any_ ++{ ++ Predicate mFn; ++}; ++ ++} // namespace detail ++} // namespace Akonadi ++ ++// Generic operator| for To_<> convertor ++template<typename RangeLike, template<typename> class OutContainer, typename T = typename RangeLike::value_type> ++auto operator|(const RangeLike &range, const Akonadi::detail::To_<OutContainer> &) -> OutContainer<T> ++{ ++ using namespace Akonadi::detail; ++ return copyContainer<RangeLike, OutContainer<T>>(range); ++} ++ ++// Specialization for case when InContainer and OutContainer are identical ++// Create a copy, but for Qt container this is very cheap due to implicit sharing. ++template<template<typename> class InContainer, typename T> ++auto operator|(const InContainer<T> &in, const Akonadi::detail::To_<InContainer> &) -> InContainer<T> ++{ ++ return in; ++} ++ ++// Generic operator| for ToAssoc_<> convertor ++template<typename RangeLike, template<typename, typename> class OutContainer, ++ typename T = typename RangeLike::value_type> ++auto operator|(const RangeLike &range, const Akonadi::detail::ToAssoc_<OutContainer> &) -> ++ OutContainer<typename T::first_type, typename T::second_type> ++{ ++ using namespace Akonadi::detail; ++ return copyAssocContainer<RangeLike, OutContainer<typename T::first_type, typename T::second_type>>(range); ++} ++ ++// Generic operator| for transform() ++template<typename RangeLike, typename TransformFn> ++auto operator|(const RangeLike &range, const Akonadi::detail::Transform_<TransformFn> &t) ++{ ++ using namespace Akonadi::detail; ++ using OutIt = TransformIterator<RangeLike, TransformFn>; ++ return Range<OutIt>(OutIt(std::cbegin(range), t.mFn, range), OutIt(std::cend(range), t.mFn, range)); ++} ++ ++// Generic operator| for filter() ++template<typename RangeLike, typename PredicateFn> ++auto operator|(const RangeLike &range, const Akonadi::detail::Filter_<PredicateFn> &p) ++{ ++ using namespace Akonadi::detail; ++ using OutIt = FilterIterator<RangeLike, PredicateFn>; ++ return Range<OutIt>(OutIt(std::cbegin(range), std::cend(range), p.mFn, range), ++ OutIt(std::cend(range), std::cend(range), p.mFn, range)); ++} ++ ++// Generic operator| fo foreach() ++template<typename RangeLike, typename EachFun> ++auto operator|(const RangeLike&range, Akonadi::detail::ForEach_<EachFun> fun) ++{ ++ std::for_each(std::cbegin(range), std::cend(range), ++ [&fun](const auto &val) { ++ Akonadi::invoke(fun.mFn, val); ++ }); ++ return range; ++} ++ ++// Generic operator| for all ++template<typename RangeLike, typename PredicateFn> ++auto operator|(const RangeLike &range, Akonadi::detail::All_<PredicateFn> fun) ++{ ++ return std::all_of(std::cbegin(range), std::cend(range), fun.mFn); ++} ++ ++// Generic operator| for any ++template<typename RangeLike, typename PredicateFn> ++auto operator|(const RangeLike &range, Akonadi::detail::Any_<PredicateFn> fun) ++{ ++ return std::any_of(std::cbegin(range), std::cend(range), fun.mFn); ++} ++ ++// Generic operator| for keys ++template<typename Container> ++auto operator|(const Container &in, Akonadi::detail::Keys_) ++{ ++ using namespace Akonadi::detail; ++ using OutIt = AssociativeContainerKeyIterator<Container>; ++ return Range<OutIt>(OutIt(in.constKeyValueBegin(), in), OutIt(in.constKeyValueEnd(), in)); ++} ++ ++ ++// Generic operator| for values ++template<typename Container> ++auto operator|(const Container &in, Akonadi::detail::Values_) ++{ ++ using namespace Akonadi::detail; ++ using OutIt = AssociativeContainerValueIterator<Container>; ++ return Range<OutIt>(OutIt(in.constKeyValueBegin(), in), OutIt(in.constKeyValueEnd(), in)); ++} ++ ++ ++namespace Akonadi { ++ ++/// Non-lazily convert given range or container to QVector ++static constexpr auto toQVector = detail::To_<QVector>{}; ++/// Non-lazily convert given range or container to QSet ++static constexpr auto toQSet = detail::To_<QSet>{}; ++/// Non-lazily convert given range or container to QList ++static constexpr auto toQList = detail::To_<QList>{}; ++/// Non-lazily convert given range or container of pairs to QMap ++static constexpr auto toQMap = detail::ToAssoc_<QMap>{}; ++/// Lazily extract values from an associative container ++static constexpr auto values = detail::Values_{}; ++/// Lazily extract keys from an associative container ++static constexpr auto keys = detail::Keys_{}; ++ ++/// Lazily transform each element of a range or container using given transformation ++template<typename TransformFn> ++detail::Transform_<TransformFn> transform(TransformFn &&fn) ++{ ++ return detail::Transform_<TransformFn>{std::forward<TransformFn>(fn)}; ++} ++ ++/// Lazily filters a range or container by applying given predicate on each element ++template<typename PredicateFn> ++detail::Filter_<PredicateFn> filter(PredicateFn &&fn) ++{ ++ return detail::Filter_<PredicateFn>{std::forward<PredicateFn>(fn)}; ++} ++ ++/// Non-lazily call EachFun for each element of the container or range ++template<typename EachFun> ++detail::ForEach_<EachFun> forEach(EachFun &&fn) ++{ ++ return detail::ForEach_<EachFun>{std::forward<EachFun>(fn)}; ++} ++ ++/// Create a range, a view on a container from the given pair fo iterators ++template<typename Iterator1, typename Iterator2, ++ typename It = std::remove_reference_t<Iterator1> ++ > ++detail::Range<It> range(Iterator1 begin, Iterator2 end) ++{ ++ return detail::Range<It>(std::move(begin), std::move(end)); ++} ++ ++/// Non-lazily check that all elements in the range satisfy given predicate ++template<typename Predicate> ++detail::All_<Predicate> all(Predicate &&fn) ++{ ++ return detail::All_<Predicate>{std::forward<Predicate>(fn)}; ++} ++ ++/// Non-lazily check that at least one element in range satisfies the given predicate ++template<typename Predicate> ++detail::Any_<Predicate> any(Predicate &&fn) ++{ ++ return detail::Any_<Predicate>{std::forward<Predicate>(fn)}; ++} ++ ++} // namespace Akonadi ++ ++#endif +diff --git a/src/shared/akscopeguard.h b/src/shared/akscopeguard.h +new file mode 100644 +index 000000000..e371383ee +--- /dev/null ++++ b/src/shared/akscopeguard.h +@@ -0,0 +1,52 @@ ++/* ++ Copyright (C) 2019 Daniel Vrátil <dvra...@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_AKSCOPEGUARD_H_ ++#define AKONADI_AKSCOPEGUARD_H_ ++ ++#include <functional> ++#include <type_traits> ++ ++namespace Akonadi { ++ ++class AkScopeGuard ++{ ++public: ++ template<typename U> ++ AkScopeGuard(U &&fun) ++ : mFun(std::move(fun)) ++ {} ++ ++ AkScopeGuard(const AkScopeGuard &) = delete; ++ AkScopeGuard(AkScopeGuard &&) = default; ++ AkScopeGuard &operator=(const AkScopeGuard &) = delete; ++ AkScopeGuard &operator=(AkScopeGuard &&) = delete; ++ ++ ~AkScopeGuard() ++ { ++ mFun(); ++ } ++ ++private: ++ std::function<void()> mFun; ++}; ++ ++} // namespace Akonadi ++ ++#endif +diff --git a/src/shared/aktraits.h b/src/shared/aktraits.h +new file mode 100644 +index 000000000..67cd60c40 +--- /dev/null ++++ b/src/shared/aktraits.h +@@ -0,0 +1,165 @@ ++/* ++ Copyright (C) 2019 Daniel Vrátil <dvra...@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_AKTRAITS_H_ ++#define AKONADI_AKTRAITS_H_ ++ ++#include <type_traits> ++#include <utility> ++ ++namespace Akonadi { ++namespace AkTraits { ++ ++namespace detail { ++ ++ /// Helpers from C++17 ++ template<typename ...> ++ using void_t = void; ++ ++ template<typename ...> ++ struct conjunction : std::true_type {}; ++ template<typename T> ++ struct conjunction<T> : T {}; ++ template<typename T, typename ... Ts> ++ struct conjunction<T, Ts...> : std::conditional_t<bool(T::value), conjunction<Ts...>, T> {}; ++ ++ #define DECLARE_HAS_MEBER_TYPE(type_name) \ ++ template<typename T, typename U = void_t<>> \ ++ struct hasMember_##type_name { \ ++ static constexpr bool value = false; \ ++ }; \ ++ \ ++ template<typename T> \ ++ struct hasMember_##type_name<T, void_t<typename T:: type_name>> : std::true_type {}; ++ ++ DECLARE_HAS_MEBER_TYPE(value_type) ++ ++ /// TODO: Use Boost TTI instead? ++ #define DECLARE_HAS_METHOD_GENERIC_IMPL(name, fun, sign) \ ++ template<typename T, typename F = sign> \ ++ struct hasMethod_##name { \ ++ public: \ ++ template<typename UType, UType> \ ++ struct helperClass; \ ++ \ ++ using True = char; \ ++ using False = struct { char dummy_[2]; }; \ ++ \ ++ template<typename U> \ ++ static True helper(helperClass<F, &U::fun>*);\ ++ template<typename> \ ++ static False helper(...); \ ++ public: \ ++ static constexpr bool value = sizeof(helper<T>(nullptr)) == sizeof(True); \ ++ }; ++ ++ #define DECLARE_HAS_METHOD_GENERIC_CONST(fun, R, ...) \ ++ DECLARE_HAS_METHOD_GENERIC_IMPL(fun##_const, fun, R(T::*)(__VA_ARGS__) const) ++ ++ #define DECLARE_HAS_METHOD_GENERIC(fun, R, ...) \ ++ DECLARE_HAS_METHOD_GENERIC_IMPL(fun, fun, R(T::*)(__VA_ARGS__)) ++ ++ DECLARE_HAS_METHOD_GENERIC_CONST(size, int, void) ++ DECLARE_HAS_METHOD_GENERIC(push_back, void, const typename T::value_type &) ++ DECLARE_HAS_METHOD_GENERIC(insert, typename T::iterator, const typename T::value_type &) ++ DECLARE_HAS_METHOD_GENERIC(reserve, void, int) ++ ++ #define DECLARE_HAS_FUNCTION(name, fun) \ ++ template<typename T> \ ++ struct has_##name { \ ++ template<typename U> \ ++ struct helperClass; \ ++ \ ++ using True = char; \ ++ using False = struct { char dummy_[2]; }; \ ++ \ ++ template<typename U> \ ++ static True helper(helperClass<decltype(fun(std::declval<T>()))>*); \ ++ template<typename> \ ++ static False helper(...); \ ++ public: \ ++ static constexpr bool value = sizeof(helper<T>(nullptr)) == sizeof(True); \ ++ }; ++ ++ // For some obscure reason QVector::begin() actually has a default ++ // argument, but QList::begin() does not, thus a regular hasMethod_* check ++ // won't cut it here. Instead we check whether the container object can be ++ // used with std::begin() and std::end() helpers. ++ // Check for constness can be performed by passing "const T" to the type. ++ DECLARE_HAS_FUNCTION(begin, std::begin) ++ DECLARE_HAS_FUNCTION(end, std::end) ++ ++ /// This is a very incomplete set of Container named requirement, but I'm ++ /// too lazy to implement all of them, but this should be good enough to match ++ /// regular Qt containers and /not/ match arbitrary non-container types ++ template<typename T> ++ struct isContainer : conjunction< ++ std::is_constructible<T>, ++ hasMember_value_type<T>, ++ has_begin<T>, ++ has_begin<const T>, ++ has_end<T>, ++ has_end<const T>, ++ hasMethod_size_const<T> ++ > {}; ++ ++ /// Matches anything that is a container and has push_back() method. ++ template<typename T> ++ struct isAppendable : conjunction< ++ isContainer<T>, ++ hasMethod_push_back<T> ++ > {}; ++ ++ /// Matches anything that is a container and has insert() method. ++ template<typename T> ++ struct isInsertable : conjunction< ++ isContainer<T>, ++ hasMethod_insert<T> ++ > {}; ++ ++ /// Matches anything that is a container and has reserve() method. ++ template<typename T> ++ struct isReservable : conjunction< ++ isContainer<T>, ++ hasMethod_reserve<T> ++ > {}; ++} ++ ++template<typename T> ++constexpr bool isAppendable = detail::isAppendable<T>::value; ++ ++template<typename T> ++constexpr bool isInsertable = detail::isInsertable<T>::value; ++ ++template<typename T> ++constexpr bool isReservable = detail::isReservable<T>::value; ++ ++} ++} ++ ++#define AK_PP_CAT_(X, Y) X ## Y ++#define AK_PP_CAT(X, Y) AK_PP_CAT_(X, Y) ++ ++#define AK_REQUIRES(...) \ ++ bool AK_PP_CAT(_ak_requires_, __LINE__) = false, \ ++ std::enable_if_t< \ ++ AK_PP_CAT(_ak_requires_, __LINE__) || (__VA_ARGS__) \ ++ >* = nullptr ++ ++#endif +-- +2.23.0 + diff -Nru akonadi-18.08.3/debian/patches/Fix-unhandled-exception-from-DataStream-operator.patch akonadi-18.08.3/debian/patches/Fix-unhandled-exception-from-DataStream-operator.patch --- akonadi-18.08.3/debian/patches/Fix-unhandled-exception-from-DataStream-operator.patch 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-18.08.3/debian/patches/Fix-unhandled-exception-from-DataStream-operator.patch 2019-08-29 20:58:54.000000000 +0200 @@ -0,0 +1,39 @@ +From 40a43c8860ebfad49b06b8db75ac4f7431d19e49 Mon Sep 17 00:00:00 2001 +From: Filipe Azevedo <pas...@gmail.com> +Date: Fri, 22 Mar 2019 22:11:10 +0100 +Subject: [PATCH 2/2] Fix unhandled exception from DataStream::operator<< + +Summary: Depends on D19982 + +Reviewers: dvratil + +Reviewed By: dvratil + +Subscribers: cfeck, kde-pim + +Tags: #kde_pim + +Differential Revision: https://phabricator.kde.org/D19983 + +(cherry picked from commit 06f1a32b5a5aa3f5a5dcf842e8de1358480fede5) +--- + src/core/connection.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core/connection.cpp b/src/core/connection.cpp +index 7eeed3f83..df51240df 100644 +--- a/src/core/connection.cpp ++++ b/src/core/connection.cpp +@@ -334,8 +334,8 @@ void Connection::doSendCommand(qint64 tag, const Protocol::CommandPtr &cmd) + + if (mSocket && mSocket->isOpen()) { + Protocol::DataStream stream(mSocket.data()); +- stream << tag; + try { ++ stream << tag; + Protocol::serialize(mSocket.data(), cmd); + } catch (const Akonadi::ProtocolException &e) { + qCWarning(AKONADICORE_LOG) << "Protocol Exception:" << QString::fromUtf8(e.what()); +-- +2.23.0 + diff -Nru akonadi-18.08.3/debian/patches/ItemSync-skip-handling-remote-items-if-local-changes.patch akonadi-18.08.3/debian/patches/ItemSync-skip-handling-remote-items-if-local-changes.patch --- akonadi-18.08.3/debian/patches/ItemSync-skip-handling-remote-items-if-local-changes.patch 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-18.08.3/debian/patches/ItemSync-skip-handling-remote-items-if-local-changes.patch 2019-08-28 18:40:42.000000000 +0200 @@ -0,0 +1,47 @@ +From 46d5d3fe22f80b0452c0dffe02a48ca30a840697 Mon Sep 17 00:00:00 2001 +From: David Faure <fa...@kde.org> +Date: Sun, 7 Apr 2019 14:07:00 +0200 +Subject: [PATCH 2/2] ItemSync: skip handling remote items if local changes + failed + +Summary: +The infamous "Multiple merge candidates" error would still leave ItemSync +in a forever-stuck state when mRemoteItemsQueue was not empty. + +Reproduced by adding a unittest that calls setFullSyncItems (with +a duplicate item), while the existing unittest for a duplicate item +was callling setIncrementalSyncItems(). + +Test Plan: Unittest passes. + +Reviewers: dvratil, vkrause + +Reviewed By: dvratil + +Subscribers: asn, kde-pim + +Tags: #kde_pim + +Differential Revision: https://phabricator.kde.org/D20243 + +(cherry picked from commit 257598195d09b1022ebf08cd58245cd3493f5a2e) +(cherry picked from commit 4fb179ce46ad3e7da8b25c921b0c8a68f9dfb4ee) +--- + src/core/itemsync.cpp | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/core/itemsync.cpp b/src/core/itemsync.cpp +index d01db1a80..538f87574 100644 +--- a/src/core/itemsync.cpp ++++ b/src/core/itemsync.cpp +@@ -442,6 +442,7 @@ void ItemSyncPrivate::slotLocalChangeDone(KJob *job) + { + if (job->error()) { + qCWarning(AKONADICORE_LOG) << "Creating/updating items from the akonadi database failed:" << job->errorString(); ++ mRemoteItemQueue.clear(); // don't try to process any more items after a rollback + } + mPendingJobs--; + mProgress++; +-- +2.23.0 + diff -Nru akonadi-18.08.3/debian/patches/series akonadi-18.08.3/debian/patches/series --- akonadi-18.08.3/debian/patches/series 2019-02-08 23:19:51.000000000 +0100 +++ akonadi-18.08.3/debian/patches/series 2019-08-30 12:26:27.000000000 +0200 @@ -2,3 +2,8 @@ disable_secure_file_priv_check.diff enable_debianabimanager.diff Call-QSqlQuery-finish-on-all-SELECT-queries-when-don.patch +Akonadi-fix-dangling-transaction-after-itemsync-fail.patch +ItemSync-skip-handling-remote-items-if-local-changes.patch +Automatic-recovery-from-Multiple-Merge-Candidates-er.patch +Fix-unhandled-exception-from-DataStream-operator.patch +Backport-missing-files-from-master.patch
signature.asc
Description: This is a digitally signed message part.