Skip to content

Shared calendar ICS export: line folding sometimes consumes a content space (RFC 5545), varying between requests #513

Description

@ghxm

Summary

The ICS feed served for a shared/public Proton Calendar link (PRODID:-//Proton AG//ProtonCalendar 1.0.0//EN) folds long content lines at 75 octets per RFC 5545. When the fold boundary lands on a space inside the text, the exporter sometimes uses that content space as the fold marker instead of inserting an additional one. RFC 5545 §3.1 unfolding (remove CRLF plus one whitespace) then deletes a character that belongs to the content.

The folding also differs between requests for identical event content, so consumers see the text flip between correct and corrupted.

Observed bytes

A DESCRIPTION containing the text Panel talk "Jazz für alle?" was served in two variants, fetched ~10 minutes apart with no event modification in between ($ marks CRLF, · marks a space):

Fetch A (correct — content space kept at the end of the folded line, marker space added):

DESCRIPTION:url: ...\nPanel talk "Jazz für·$
·alle?" + Fiona Grond & Luca Zambito + ...$

Fetch B (broken — the content space was moved to the continuation line, where it doubles as the fold marker):

DESCRIPTION:url: ...\nPanel talk "Jazz für$
·alle?" + Fiona Grond & Luca Zambito + ...$

Compliant unfolding of Fetch B yields Panel talk "Jazz füralle?".

Supporting statistics

Fold-line octet lengths across the same feed (~6700 VEVENTs, ~2360 folds), measured on the two fetches:

  • Fetch A: 75 octets ×2362, 74 ×10, 73 ×2 (the short ones are legitimate multi-byte UTF-8 characters that would straddle the 75-octet boundary)
  • Fetch B: 75 ×2225, 74 ×127, 73 ×4, 72 ×1

The ~120 additional short folds in Fetch B are exactly the cases where the next character would still have fit within 75 octets — i.e. the fold was taken early on a space, consuming it. Affected spots in Fetch B include Jazz für|alle? and Hennicker-Schmidt| – Anton Kaun (space before an en dash).

Expected

Folding must be content-preserving: the inserted CRLF + single whitespace must be additional bytes, so that unfolding reproduces the original text exactly, regardless of where the boundary falls. Identical content should also fold identically across requests.

Impact

Any RFC-compliant consumer (tested with Python icalendar, but this is inherent to the spec, not a parser quirk) loses spaces from event descriptions, summaries, and locations at unpredictable positions that change between fetches. The information is unrecoverable for the consumer in the general case.

Environment

  • Shared calendar URL export (calendar.proton.me/api/calendar/v1/url/.../calendar.ics)
  • PRODID:-//Proton AG//ProtonCalendar 1.0.0//EN, VERSION:2.0
  • Observed 2026-06-12; reproducible by re-fetching a feed with long description lines until the fold boundary shifts onto a space

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions