Caldav calendar color inconsistency

Issue Description

Stalwart allow setting invalid caldav color values and subsequently does not behave normally any more regarding this property.

Expected Behavior

I think stalwart should reject invalid properties that it uses internally.

Actual Behavior

The invalid value is accepted and can later on not be changed in standard ways.

Reproduction Steps

When creating a calendar via caldav with an invalid color property value (please don’t ask) like this

<?xml version="1.0" encoding="utf-8"?>
<D:mkcol xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
  <D:set>
    <D:prop>
      <D:resourcetype>
        <D:collection/>
        <C:calendar/>
      </D:resourcetype>
		<calendar-color xmlns="http://apple.com/ns/ical/">#</calendar-color>
    </D:prop>
  </D:set>
</D:mkcol>

Stalwart actually stores this information somehow, and it will be reported back as is for PROPFIND.
Internally, however, the color is not set (empty input field in the ui). It is subsequently possible to change the color in the stalwart ui (which also propagates via jmap, I suppose), but not for caldav, which will still show the invalid value. Additionally, it is not impossible to set the color via carddav PROPPATCH

<?xml version="1.0" encoding="utf-8"?>
<d:propertyupdate xmlns:d="DAV:">
  <d:set>
    <d:prop>
      <calendar-color xmlns="http://apple.com/ns/ical/">#0000ff</calendar-color>
    </d:prop>
  </d:set>
</d:propertyupdate>

Only after removing the property altogether via PROPPATCH

<d:propertyupdate xmlns:d="DAV:">
  <d:remove>
    <d:prop>
      <calendar-color xmlns="http://apple.com/ns/ical/"/>
    </d:prop>
  </d:remove>
</d:propertyupdate>

It starts behaving correctly again.

Stalwart Version

v0.16.x

Installation Method

Docker

Database Backend

RocksDB

Blob Storage

RocksDB

Search Engine

Internal

Directory Backend

Internal

I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.

on

I have searched this support forum (open and closed topics) and confirm this is not a duplicate.

on

I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.

on

Thanks for the detailed report and reproduction steps. Looking at the source, calendar color is stored as a plain Option<String> on the calendar preferences record with no format validation applied when it comes in via CalDAV MKCOL/PROPPATCH, so a value like # is accepted and stored as-is (and echoed back verbatim on PROPFIND) instead of being validated as a proper CSS/iCal color and rejected. This matches your description: the JMAP/UI path treats an invalid value differently from the CalDAV dead-property/preference path, which is why it can look “empty” internally while CalDAV keeps returning the bad value until it’s explicitly removed with a PROPPATCH remove.

This is a genuine validation gap rather than something covered by current documentation, so I can’t point you to a doc that addresses it. I’ve flagged the report as reproducible against the current code; a staff or community member will follow up on fixing the validation.

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.

Stalwart doesn’t interpret calendar-color. It’s Apple’s property, so the server keeps whatever you send as an opaque WebDAV dead property and hands it back on PROPFIND without parsing it. The color you see in the web UI (and over JMAP) is a separate field, and nothing maps one onto the other. So the empty UI field and the UI change never reaching CalDAV have nothing to do with #; a valid color in the MKCOL would have behaved the same. Linking the two is a reasonable request.

The PROPPATCH set not taking is the part I can’t explain. A set on an existing dead property removes the old element and adds the new one, which is what your remove followed by set does by hand. To see where it goes wrong I’d need the multistatus response to the PROPPATCH that didn’t take, a PROPFIND on calendar-color straight after it, and the exact version you’re running.