free_busy_report() explicitly handles [reporting] max_freebusy_occurrence = 0
as "limit disabled" when fetching occurrences (n=0 lets time_range_fill
return all occurrences), but the subsequent limit check
'len(occurrences) >= max_occurrence' is trivially true for
max_occurrence == 0, so every free-busy query on a non-empty calendar
raised ValueError ("FREEBUSY occurrences limit of 0 hit") and was
answered with HTTP 400.
Skip the limit check when the limit is disabled, consistent with how
xml_report() treats the same setting ('if max_occurrence and ...').
Behavior for positive limits is unchanged.
Co-Authored-By: Claude <noreply@anthropic.com>
The share-by-map BDAY-to-ICS conversion builds a placeholder mapping for
the SUMMARY/DESCRIPTION/alarm templates. Each block sets the fallback
marker for its own placeholder (e.g. {nickname} -> '!nickname!', {n:f} ->
'!n:f!'), but the {fn} block wrote the empty-FN fallback to {nickname}
instead of {fn}.
As a result, for a VCARD whose FN is present but empty, a genuine
NICKNAME was overwritten with '!fn!' in the generated event, and the
{fn} placeholder never received its '!fn!' marker, so a '[{fn}|...]'
fallback in a template failed to resolve.
Assign the fallback to {fn}, matching the surrounding blocks.
Co-Authored-By: Claude <noreply@anthropic.com>
A CardDAV addressbook-query REPORT with a text-match prop-filter on a
structured property (e.g. N or ADR) returned HTTP 500. vobject parses
these into Name/Address objects rather than plain strings, so text_match
called .lower() on a non-string and raised AttributeError. Coerce
non-string values to their text representation before matching.
Co-Authored-By: Claude <noreply@anthropic.com>
Assert the response is a dict before indexing, matching the existing
pattern used elsewhere in test_expand.py, so the Lint (mypy) job passes.
Co-Authored-By: Claude <noreply@anthropic.com>
_strip_single_event() removed the recurrence-defining properties (RRULE,
EXDATE, EXRULE, RDATE) with a single try/except around sequential
delattr() calls. When one of them was absent (e.g. an event with no
EXDATE), the AttributeError aborted the whole block and the following
properties -- notably RDATE -- were left on the expanded
single-occurrence VEVENTs returned by a calendar-data expand REPORT.
Remove each property independently so a missing one no longer prevents
removal of the others. Add a regression test and fixture.
Co-Authored-By: Claude <noreply@anthropic.com>
The new TSC compiler does stricter checking on the JS documentation
strings. All documentation strings have been updated to pass with
the current version (7.0.2)
In addition, the version of the TSC compiler used during the github
action will be fixed to 7.0.2, so that we don't get these type of
sudden errors again in the future. Unfortunately this means we need
to periodically update this manually.
A calendar-query REPORT with a time-range filter failed to return a
VEVENT that has a whole-day DURATION (e.g. DURATION:P1D or P2D) whenever
the queried range fell inside the event but after DTSTART.
The VEVENT time-range logic in radicale/item/filter.py gated the
"non-zero duration" branch (rfc4791-9.9 line 2) on timedelta.seconds
instead of timedelta.total_seconds(). For a duration that is an exact
multiple of 24h, timedelta.seconds is 0 (the days component holds the
value), so the event was treated as zero-length (line 3) and only
matched a one-second window at its start. An identical event expressed
with DTEND matched correctly, confirming the defect is isolated to the
DURATION path.
Use total_seconds() so multi-day durations are handled correctly. Adds a
regression test (event11, DURATION:P2D) covering both an inside-range
match and an outside-range non-match.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Append email domain to login before trying connect to the IMAP server. Use this instead of strip_domain,
which removes the domain before trying the IMAP connection, to avoid the domain in the folder names of the collection.
make_href percent-encodes hrefs (an '@' in an email principal becomes
%40), but the backmap compared against the raw PathMapped, so the
rewrite was skipped and the owner's real path leaked -- editing a shared
collection then failed with 403. Compare and rewrite on the quoted form.