Fix vCard 3 REV and KEY value handling

Bring the remaining vCard 3 REV and KEY behavior into line with RFC
2426.

For REV, serialize VALUE=date using the date form instead of incorrectly
emitting VALUE=date-time with a timestamp payload.

For KEY, stop defaulting constructed values to VALUE=uri. The vCard 3
specification defines KEY as binary by default and allows it to be reset
to text, but not to uri. Tighten both construction and parsing
accordingly: reject VALUE=uri for KEY, enforce the relationship between
VALUE=binary and ENCODING=b, and reject VALUE=text when ENCODING=b is
present.

Update the regression coverage to reflect the spec boundary: PHOTO,
LOGO, and SOUND may round-trip as uris; KEY may contain text that looks
like a URI; KEY does not allow VALUE=uri; and vCard 3 KEY parameters
still require name=value syntax.

AI-Assisted: yes
AI-Tool: OpenAI Codex / gpt-5.4 xhigh
This commit is contained in:
2026-03-28 10:46:37 -05:00
parent 6e6e06bdc4
commit 8f2a05cde6
2 changed files with 50 additions and 7 deletions

View File

@@ -182,21 +182,44 @@ suite "vcard/vcard3":
serialized.contains("SOUND;ENCODING=b;TYPE=WAVE:" & payload)
serialized.contains("KEY;ENCODING=b;TYPE=PGP:" & payload)
test "spec: uri-backed binary properties round-trip as uris":
test "spec: PHOTO, LOGO, and SOUND may round-trip as uris":
let serialized = $parseSingleVCard3(vcard3Doc(
"VERSION:3.0",
"FN:John Smith",
"N:Smith;John;;;",
"PHOTO;VALUE=uri:http://example.test/photo.jpg",
"LOGO;VALUE=uri:http://example.test/logo.gif",
"SOUND;VALUE=uri:http://example.test/sound.wav",
"KEY;VALUE=uri:http://example.test/key.asc"))
"SOUND;VALUE=uri:http://example.test/sound.wav"))
check:
serialized.contains("PHOTO;VALUE=uri:http://example.test/photo.jpg")
serialized.contains("LOGO;VALUE=uri:http://example.test/logo.gif")
serialized.contains("SOUND;VALUE=uri:http://example.test/sound.wav")
serialized.contains("KEY;VALUE=uri:http://example.test/key.asc")
test "spec: KEY does not allow uri values":
expect(VCardParsingError):
discard parseVCards(vcard3Doc(
"VERSION:3.0",
"FN:John Smith",
"N:Smith;John;;;",
"KEY;VALUE=uri:http://example.test/key.asc"))
test "spec: KEY may contain text values that look like uris":
let serialized = $parseSingleVCard3(vcard3Doc(
"VERSION:3.0",
"FN:John Smith",
"N:Smith;John;;;",
"KEY;TYPE=PGP:http://example.test/key.pgp"))
check serialized.contains("KEY;TYPE=PGP:http://example.test/key.pgp")
test "spec: KEY parameters must use name=value syntax in vCard 3":
expect(VCardParsingError):
discard parseVCards(vcard3Doc(
"VERSION:3.0",
"FN:John Smith",
"N:Smith;John;;;",
"KEY;PGP:http://example.test/key.pgp"))
test "spec: quoted parameter values are accepted":
let parsed = parseSingleVCard3(vcard3Doc(