Skip to content

Fix/caldav option forgiving#62090

Open
tlilja wants to merge 2 commits into
nextcloud:masterfrom
tlilja:fix/caldav-option-forgiving
Open

Fix/caldav option forgiving#62090
tlilja wants to merge 2 commits into
nextcloud:masterfrom
tlilja:fix/caldav-option-forgiving

Conversation

@tlilja

@tlilja tlilja commented Jul 14, 2026

Copy link
Copy Markdown

Summary

This is a self-contained PR that adds support for using SabreDAV's OPTION_FORGIVING when dav.forigiving_ical_parser config option is set to allow non-standard ICS events parsed instead of giving error.

For example, Outlook on Mac, under certain conditions, generates ICS events that have invalid key X-ENTOURAGE_UID (underscore should be dash) which get rejected by Nextcloud/SabreDAV's strict parser by default.

This PR requires no changes to SabreDAV unlike the closed PR nextcloud/3rdparty#902 which got subsequently pushed to SabreDAV (sabre-io/dav#1563)

This PR includes unit tests and all DAV tests pass with the changes.

A bit problematic part of this PR is that overriding \Sabre\CalDAV\Plugin::validateICalendar() just to replace the VObject\Reader::read() call which is prone to error if SabreDAV changes their validateICalendar(). A better approach would be to refactor \Sabre\CalDAV\Plugin to allow overriding only the VObject\Reader::read() invocationa arguments with a hook but that would require changes on SabreDAV side whereas this is fully self-contained to Nextcloud.

TODO

  • Add documentation

Checklist

AI (if applicable)

  • The content of this PR was partly or fully generated using AI

@tlilja tlilja requested review from Altahrim, ArtificialOwl, icewind1991 and provokateurin and removed request for a team July 14, 2026 12:23
@tlilja tlilja force-pushed the fix/caldav-option-forgiving branch from a4141cf to dc5b8d5 Compare July 14, 2026 13:08
Comment on lines +348 to 353
private function getReaderOptions(): int {
return $this->config->getSystemValueBool('dav.forgiving_ical_parser', false)
? Reader::OPTION_FORGIVING
: 0;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be an import option, not a system configuration value

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, that makes sense, ImportOption is a better approach for andling the ImportService code path, but what about the main problem this PR is addressing, i.e., if I get the non-standard ICS from CalDAV PUT which goes through Plugin::validateICalendar?

Should the IConfig be used for that though?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll the issue you linked specifically states that the issue is with importing. Not with general PUT requests from a specific client, so I would not add a plugin to validate the events.

Also there is a PR that will fix a lot of this in the future.

#55178
nextcloud/calendar#8483


$calendarObjects = array_map(function ($o) use ($options) {
$calendarData = Reader::read($o['calendardata']);
$calendarData = Reader::read($o['calendardata'], $this->getReaderOptions());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed, once the data is read the first time with the forgiving flag, it should not need to be read with this flag.

*/
public function getDenormalizedData(string $calendarData): array {
$vObject = Reader::read($calendarData);
$vObject = Reader::read($calendarData, $this->getReaderOptions());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

@@ -3843,7 +3843,13 @@ public function moveCalendar($uriName, $uriOrigin, $uriDestination, $newUriName
* @return VCalendar
*/
protected function readCalendarData($objectData) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

$vObject->add(clone $timezones[$zone]);
}
}
yield $vObject;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, once the object is read, once, the correct object exists and is serialized back to a proper object, it should not need to be run using the flag.

@tlilja tlilja Jul 14, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough, I should have referred to case #45574 as the main issue instead of #16679 (I just updated the description to reflect this.).

For me, the main problem is that my users receive non-standard ICS invites by email (usually coming from Outlook) and hence the PR.

Fixing the import seemed like a logical thing to do as well, but perhaps I should drop this and open a new PR that addresees just this `CalDAV PUT' and case #45574.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Event updates are not handled by any of the code in this PR. so this would not fix your issue.

Also the most likely issue with Outook invitations is a missing Organizer property. which I already made a work around for.

Can you share one of the non working invitations with me?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test-entourage.ics.txt

I attached an ICS file that gives 415 Unsupported Media Type error when CalDAV PUT is done against master and 201 Created when done against fix/caldav-option-forgiving.

See also the transcript in https://gist.github.com/tlilja/2b3f7a26c816d730cd0d6a260cc77809

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I see the issue the "X-ENTOURAGE_UUID" property has a underscore.

How is the CalDav put done? A import?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the CalDAV PUT handling is done by apps/dav/lib/CalDav/Plugin.php, specifically VObject\Reader::read() call in Plugin.php:65 in this PR.

So the code path is different from Import which, though, invokes the same VObject\Reader::read().

@tlilja tlilja Jul 14, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That code in Plugin.php is the validation only.

The actual storing/updating the calendar events to the database is done by two CalDavBackend methods createCalendarObject() and updateCalendarObject() in apps/dav/lib/CalDav/CalDavBackend.php.

Timo Lilja added 2 commits July 14, 2026 16:21
…g_ical_parser is set

Clients such as Outlook for Mac produce property names with underscores
(e.g. X-ENTOURAGE_UUID), which are invalid per RFC 5545 §3.2 and cause
sabre to throw a ParseException, resulting in a 415 response.

When the system config key dav.forgiving_ical_parser is true, pass
Sabre\VObject\Reader::OPTION_FORGIVING to every Reader::read() call
site in the CalDAV stack (Plugin::validateICalendar, CalDavBackend,
ImportService). Non-conforming property names are then silently ignored
rather than rejected.

Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Timo Lilja <timo.lilja@iki.fi>
…d and ImportService

Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Timo Lilja <timo.lilja@iki.fi>
@tlilja tlilja force-pushed the fix/caldav-option-forgiving branch from dc5b8d5 to 28f91d2 Compare July 14, 2026 13:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Parse error: Invalid Mimedir file ICS import fails - OPTION_FORGIVING should handle underscores

2 participants