[pim/akonadi-calendar] src: Fix wrongfully incrementing incidence SEQUENCE when accepting iTIP invitation
Dominique MICHEL <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit b6a07dc0b9597ef2c8daab58b43835ad1e6c8568 by Dominique MICHEL, on behalf of Dominique MICHEL.
Committed on 07/08/2026 at 15:33.
Pushed by aloeil into branch 'master'.
Fix wrongfully incrementing incidence SEQUENCE when accepting iTIP invitation
When accepting an iTIP invitation, we must not increment the SEQUENCE
property. It's managed by the organizer who has already incremented it
before sending it.
M +11 -4 src/calendarbase.cpp
M +3 -2 src/calendarbase_p.h
M +68 -54 src/incidencechanger.cpp
M +1 -0 src/incidencechanger.h
M +8 -0 src/incidencechanger_p.h
A +21 -0 src/incidencemodificationpolicy_p.h [License: LGPL(v2.0+)]
M +1 -1 src/scheduler_p.cpp
https://invent.kde.org/pim/akonadi-calendar/-/commit/b6a07dc0b9597ef2c8daab58b43835ad1e6c8568
diff --git a/src/calendarbase.cpp b/src/calendarbase.cpp
index c20e956b..626398b6 100644
--- a/src/calendarbase.cpp
+++ b/src/calendarbase.cpp
@@ -10,6 +10,8 @@
#include "calendarbase_p.h"
#include "calendarutils.h"
#include "incidencechanger.h"
+#include "incidencechanger_p.h"
+
#include <Akonadi/CollectionFetchJob>
#include <KLocalizedString>
@@ -352,6 +354,14 @@ void CalendarBasePrivate::handleParentChanged(const KCalendarCore::Incidence::Pt
}
}
+bool CalendarBasePrivate::modifyIncidence(const KCalendarCore::Incidence::Ptr &newIncidence, IncidenceModificationPolicy modificationPolicy)
+{
+ Q_ASSERT(newIncidence);
+ Akonadi::Item item_ = q->item(newIncidence->instanceIdentifier());
+ item_.setPayload<KCalendarCore::Incidence::Ptr>(newIncidence);
+ return -1 != mIncidenceChanger->d->modifyIncidence(item_, modificationPolicy);
+}
+
CalendarBase::CalendarBase(QObject *parent)
: MemoryCalendar(QTimeZone::systemTimeZone())
, d_ptr(new CalendarBasePrivate(this))
@@ -599,10 +609,7 @@ bool CalendarBase::deleteIncidence(const KCalendarCore::Incidence::Ptr &incidenc
bool CalendarBase::modifyIncidence(const KCalendarCore::Incidence::Ptr &newIncidence)
{
Q_D(CalendarBase);
- Q_ASSERT(newIncidence);
- Akonadi::Item item_ = item(newIncidence->instanceIdentifier());
- item_.setPayload<KCalendarCore::Incidence::Ptr>(newIncidence);
- return -1 != d->mIncidenceChanger->modifyIncidence(item_);
+ return d->modifyIncidence(newIncidence, IncidenceModificationPolicy::Default);
}
IncidenceChanger *CalendarBase::incidenceChanger() const
diff --git a/src/calendarbase_p.h b/src/calendarbase_p.h
index 96380d2c..67183ad6 100644
--- a/src/calendarbase_p.h
+++ b/src/calendarbase_p.h
@@ -8,6 +8,7 @@
#include "calendarbase.h"
#include "incidencechanger.h"
+#include "incidencemodificationpolicy_p.h"
#include <QList>
#include <QMultiHash>
@@ -24,13 +25,13 @@ public:
AKONADI_CALENDAR_EXPORT void internalInsert(const Akonadi::Item &item);
AKONADI_CALENDAR_EXPORT void internalRemove(const Akonadi::Item &item);
- void deleteAllIncidencesOfType(const QString &mimeType);
-
void handleUidChange(const Akonadi::Item &oldItem, const Akonadi::Item &newItem, const QString &newIdentifier);
// Checks if parent changed and adjust internal hierarchy info
void handleParentChanged(const KCalendarCore::Incidence::Ptr &incidence);
+ bool modifyIncidence(const KCalendarCore::Incidence::Ptr &newIncidence, IncidenceModificationPolicy modificationPolicy);
+
public Q_SLOTS:
void slotDeleteFinished(int changeId, const QList<Akonadi::Item::Id> &itemIds, Akonadi::IncidenceChanger::ResultCode, const QString &errorMessage);
diff --git a/src/incidencechanger.cpp b/src/incidencechanger.cpp
index 3d5b4c2e..82e1ba52 100644
--- a/src/incidencechanger.cpp
+++ b/src/incidencechanger.cpp
@@ -675,6 +675,67 @@ bool IncidenceChangerPrivate::myAttendeeStatusChanged(const Incidence::Ptr &newI
return !oldMe.isNull() && !newMe.isNull() && oldMe.status() != newMe.status();
}
+int IncidenceChangerPrivate::modifyIncidence(const Akonadi::Item &changedItem,
+ IncidenceModificationPolicy modificationPolicy,
+ const KCalendarCore::Incidence::Ptr &originalPayload,
+ QWidget *parent)
+{
+ if (!changedItem.isValid() || !changedItem.hasPayload<Incidence::Ptr>()) {
+ qCWarning(AKONADICALENDAR_LOG) << "An invalid item or payload is not allowed.";
+ cancelTransaction();
+ return -1;
+ }
+
+ if (!hasRights(changedItem.parentCollection(), IncidenceChanger::ChangeTypeModify)) {
+ qCWarning(AKONADICALENDAR_LOG) << "Item " << changedItem.id() << " can't be deleted due to ACL restrictions";
+ const int changeId = ++mLatestChangeId;
+ const QString errorString = showErrorDialog(IncidenceChanger::ResultCodePermissions, parent);
+ emitModifyFinished(q, changeId, changedItem, IncidenceChanger::ResultCodePermissions, errorString);
+ cancelTransaction();
+ return changeId;
+ }
+
+ // TODO also update revision here instead of in the editor
+ changedItem.payload<Incidence::Ptr>()->setLastModified(QDateTime::currentDateTimeUtc());
+
+ const uint atomicOperationId = mBatchOperationInProgress ? mLatestAtomicOperationId : 0;
+ const int changeId = ++mLatestChangeId;
+ auto modificationChange = new ModificationChange(q, changeId, atomicOperationId, parent);
+ modificationChange->modificationPolicy = modificationPolicy;
+ Change::Ptr const change(modificationChange);
+
+ if (originalPayload) {
+ Item originalItem(changedItem);
+ originalItem.setPayload<KCalendarCore::Incidence::Ptr>(originalPayload);
+ modificationChange->originalItems << originalItem;
+ }
+
+ modificationChange->newItem = changedItem;
+ mChangeById.insert(changeId, change);
+
+ if (!allowAtomicOperation(atomicOperationId, change)) {
+ const QString errorString = showErrorDialog(IncidenceChanger::ResultCodeDuplicateId, parent);
+
+ change->resultCode = IncidenceChanger::ResultCodeDuplicateId;
+ change->errorString = errorString;
+ cancelTransaction();
+ qCWarning(AKONADICALENDAR_LOG) << "Atomic operation now allowed";
+ return changeId;
+ }
+
+ if (mBatchOperationInProgress && mAtomicOperations[atomicOperationId]->rolledback()) {
+ const QString errorMessage = showErrorDialog(IncidenceChanger::ResultCodeRolledback, parent);
+ qCritical() << errorMessage;
+ cleanupTransaction();
+ emitModifyFinished(q, changeId, changedItem, IncidenceChanger::ResultCodeRolledback, errorMessage);
+ } else {
+ adjustRecurrence(originalPayload, CalendarUtils::incidence(modificationChange->newItem));
+ performModification(change);
+ }
+
+ return changeId;
+}
+
IncidenceChanger::IncidenceChanger(QObject *parent)
: QObject(parent)
, d(new IncidenceChangerPrivate(/**history=*/true, /*factory=*/nullptr, this))
@@ -856,59 +917,7 @@ void IncidenceChangerPrivate::deleteIncidences2(int changeId, ITIPHandlerHelper:
int IncidenceChanger::modifyIncidence(const Item &changedItem, const KCalendarCore::Incidence::Ptr &originalPayload, QWidget *parent)
{
- if (!changedItem.isValid() || !changedItem.hasPayload<Incidence::Ptr>()) {
- qCWarning(AKONADICALENDAR_LOG) << "An invalid item or payload is not allowed.";
- d->cancelTransaction();
- return -1;
- }
-
- if (!d->hasRights(changedItem.parentCollection(), ChangeTypeModify)) {
- qCWarning(AKONADICALENDAR_LOG) << "Item " << changedItem.id() << " can't be deleted due to ACL restrictions";
- const int changeId = ++d->mLatestChangeId;
- const QString errorString = d->showErrorDialog(ResultCodePermissions, parent);
- emitModifyFinished(this, changeId, changedItem, ResultCodePermissions, errorString);
- d->cancelTransaction();
- return changeId;
- }
-
- // TODO also update revision here instead of in the editor
- changedItem.payload<Incidence::Ptr>()->setLastModified(QDateTime::currentDateTimeUtc());
-
- const uint atomicOperationId = d->mBatchOperationInProgress ? d->mLatestAtomicOperationId : 0;
- const int changeId = ++d->mLatestChangeId;
- auto modificationChange = new ModificationChange(this, changeId, atomicOperationId, parent);
- Change::Ptr const change(modificationChange);
-
- if (originalPayload) {
- Item originalItem(changedItem);
- originalItem.setPayload<KCalendarCore::Incidence::Ptr>(originalPayload);
- modificationChange->originalItems << originalItem;
- }
-
- modificationChange->newItem = changedItem;
- d->mChangeById.insert(changeId, change);
-
- if (!d->allowAtomicOperation(atomicOperationId, change)) {
- const QString errorString = d->showErrorDialog(ResultCodeDuplicateId, parent);
-
- change->resultCode = ResultCodeDuplicateId;
- change->errorString = errorString;
- d->cancelTransaction();
- qCWarning(AKONADICALENDAR_LOG) << "Atomic operation now allowed";
- return changeId;
- }
-
- if (d->mBatchOperationInProgress && d->mAtomicOperations[atomicOperationId]->rolledback()) {
- const QString errorMessage = d->showErrorDialog(ResultCodeRolledback, parent);
- qCritical() << errorMessage;
- d->cleanupTransaction();
- emitModifyFinished(this, changeId, changedItem, ResultCodeRolledback, errorMessage);
- } else {
- d->adjustRecurrence(originalPayload, CalendarUtils::incidence(modificationChange->newItem));
- d->performModification(change);
- }
-
- return changeId;
+ return d->modifyIncidence(changedItem, IncidenceModificationPolicy::Default, originalPayload, parent);
}
void IncidenceChangerPrivate::performModification(const Change::Ptr &change)
@@ -980,7 +989,12 @@ void IncidenceChangerPrivate::performModification2(int changeId, ITIPHandlerHelp
Incidence::Ptr const incidence = CalendarUtils::incidence(newItem);
{
- if (!allowedModificationsWithoutRevisionUpdate(incidence)) { // increment revision ( KCalendarCore revision, not akonadi )
+ const ModificationChange::Ptr modificationChange = change.staticCast<ModificationChange>();
+ const auto modificationPolicy = modificationChange->modificationPolicy;
+ // SEQUENCE / revision is managed by the organizer, we don't increment it
+ const bool shouldIncrementRevision =
+ modificationPolicy != IncidenceModificationPolicy::Organizer && !allowedModificationsWithoutRevisionUpdate(incidence);
+ if (shouldIncrementRevision) {
const int revision = incidence->revision();
incidence->setRevision(revision + 1);
}
diff --git a/src/incidencechanger.h b/src/incidencechanger.h
index 3be6d90e..7cdc33d6 100644
--- a/src/incidencechanger.h
+++ b/src/incidencechanger.h
@@ -474,6 +474,7 @@ Q_SIGNALS:
private:
friend class HistoryPrivate;
friend class AtomicOperation;
+ friend class CalendarBasePrivate;
// used internally by the History class
explicit IncidenceChanger(bool enableHistory, QObject *parent = nullptr);
diff --git a/src/incidencechanger_p.h b/src/incidencechanger_p.h
index 7da1809c..c65b0a8c 100644
--- a/src/incidencechanger_p.h
+++ b/src/incidencechanger_p.h
@@ -14,6 +14,7 @@
using namespace Qt::Literals::StringLiterals;
#include "incidencechanger.h"
+#include "incidencemodificationpolicy_p.h"
#include "itiphandlerhelper_p.h"
#include <Akonadi/Collection>
@@ -116,6 +117,8 @@ public:
}
void emitCompletionSignal() override;
+
+ IncidenceModificationPolicy modificationPolicy = IncidenceModificationPolicy::Default;
};
class CreationChange : public Change
@@ -282,6 +285,11 @@ public:
static bool
myAttendeeStatusChanged(const KCalendarCore::Incidence::Ptr &newIncidence, const KCalendarCore::Incidence::Ptr &oldIncidence, const QStringList &myEmails);
+ int modifyIncidence(const Akonadi::Item &item,
+ IncidenceModificationPolicy modificationPolicy,
+ const KCalendarCore::Incidence::Ptr &originalPayload = KCalendarCore::Incidence::Ptr(),
+ QWidget *parent = nullptr);
+
public Q_SLOTS:
void handleCreateJobResult(KJob *job);
void handleModifyJobResult(KJob *job);
diff --git a/src/incidencemodificationpolicy_p.h b/src/incidencemodificationpolicy_p.h
new file mode 100644
index 00000000..8f6f12a4
--- /dev/null
+++ b/src/incidencemodificationpolicy_p.h
@@ -0,0 +1,21 @@
+/*
+ SPDX-FileCopyrightText: 2026 Dominique Michel <[email protected]>
+
+ SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+#pragma once
+
+namespace Akonadi
+{
+/*!
+ * This enum adds context for modification changes.
+ *
+ * \internal
+ */
+enum class IncidenceModificationPolicy {
+ Default = 0, ///< Default modification policy.
+ Organizer = 1, ///< An update received from the organizer.
+};
+
+}
diff --git a/src/scheduler_p.cpp b/src/scheduler_p.cpp
index 01ad2190..668cd136 100644
--- a/src/scheduler_p.cpp
+++ b/src/scheduler_p.cpp
@@ -258,7 +258,7 @@ void Scheduler::acceptRequest(const IncidenceBase::Ptr &incidenceBase,
}
}
- const bool success = calendar->modifyIncidence(incidence);
+ const bool success = calendar->d_ptr->modifyIncidence(incidence, IncidenceModificationPolicy::Organizer);
if (!success) {
Q_EMIT transactionFinished(ResultModifyingError, i18n("Error modifying incidence"));