Contacts: a REV in the extended form vCard 3 uses is not read, so the phone wins every both-sides conflict #25

Closed
opened 2026-10-01 10:47:53 +00:00 by tiagoagueda · 1 comment
Owner

Found on 2026-09-29 in a read-only review of postulo-dav at 97abc9779, reading the vCard timestamp parser and running it on sample cards.

parse_stamp accepts only the basic form YYYYMMDDTHHMMSSZ. RFC 2426, the vCard 3 specification, gives REV in the extended form (REV:1995-10-31T22:27:10Z), and so do clients that write vCard 3. Such a REV is read as None, and the conflict rule then treats the phone's card as changed "now", which is always later than the local edit. The later change does not win, as the README and the module docstring promise; the phone's does.

  • Where: src/postulo_dav/text.py:270-283 (_STAMP = re.compile(r"^(\d{4})(\d{2})(\d{2})(?:T(\d{2})(\d{2})(\d{2}))?(Z?)$") and parse_stamp); src/postulo_dav/sync.py:472 (if local_changed and (incoming.rev or now) < contact.updated_at:).
  • What happens: text.text_to_card("BEGIN:VCARD\r\nREV:2026-09-06T10:00:00Z\r\nEND:VCARD").rev is None (checked by running it), while the basic form parses. A contact edited on the phone at 10:00, with REV in the extended form, and edited here at 10:10, is overwritten by the phone's card at the 10:15 run; the local version survives only as a "Changed here on …" paragraph in the notes.
  • Why it matters: the newer edit made in Postulo is replaced by an older one, against the rule the plugin documents.
  • Fix: accept the extended form (dashes and colons), optional fractional seconds and a numeric UTC offset in parse_stamp. When REV is still unreadable, say in the report that the winner was chosen without the phone's time, rather than silently assuming "now".

Done when

  • A parser test in tests/test_dav.py that REV:2026-09-06T10:00:00Z and REV:2026-09-06T12:00:00+02:00 both read as 10:00 UTC, and a sync test where a card with an older extended-form REV loses to a later local edit.
Found on 2026-09-29 in a read-only review of postulo-dav at 97abc9779, reading the vCard timestamp parser and running it on sample cards. `parse_stamp` accepts only the basic form `YYYYMMDDTHHMMSSZ`. RFC 2426, the vCard 3 specification, gives REV in the extended form (`REV:1995-10-31T22:27:10Z`), and so do clients that write vCard 3. Such a REV is read as `None`, and the conflict rule then treats the phone's card as changed "now", which is always later than the local edit. The later change does not win, as the README and the module docstring promise; the phone's does. - **Where:** `src/postulo_dav/text.py:270-283` (`_STAMP = re.compile(r"^(\d{4})(\d{2})(\d{2})(?:T(\d{2})(\d{2})(\d{2}))?(Z?)$")` and `parse_stamp`); `src/postulo_dav/sync.py:472` (`if local_changed and (incoming.rev or now) < contact.updated_at:`). - **What happens:** `text.text_to_card("BEGIN:VCARD\r\nREV:2026-09-06T10:00:00Z\r\nEND:VCARD").rev` is `None` (checked by running it), while the basic form parses. A contact edited on the phone at 10:00, with REV in the extended form, and edited here at 10:10, is overwritten by the phone's card at the 10:15 run; the local version survives only as a "Changed here on …" paragraph in the notes. - **Why it matters:** the newer edit made in Postulo is replaced by an older one, against the rule the plugin documents. - **Fix:** accept the extended form (dashes and colons), optional fractional seconds and a numeric UTC offset in `parse_stamp`. When REV is still unreadable, say in the report that the winner was chosen without the phone's time, rather than silently assuming "now". ## Done when - A parser test in `tests/test_dav.py` that `REV:2026-09-06T10:00:00Z` and `REV:2026-09-06T12:00:00+02:00` both read as 10:00 UTC, and a sync test where a card with an older extended-form REV loses to a later local edit.
tiagoagueda added this to the 0.6.0 milestone 2026-10-01 10:47:53 +00:00
Author
Owner

Fixed by pull request #35, merged as fb8bbd7ae.

Fixed by pull request #35, merged as fb8bbd7ae.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo-dav#25
No description provided.