Fix vCard 3 inline binary round-tripping
Decode ENCODING=b payloads for PHOTO, LOGO, SOUND, and KEY when parsing so serializing a parsed card does not base64-encode already encoded wire data a second time. Add regression coverage for both inline binary and VALUE=uri round-trips. AI-Assisted: yes AI-Tool: OpenAI Codex /gpt-5.4 xhigh
This commit is contained in:
@@ -963,6 +963,20 @@ proc readTextValueList(
|
|||||||
result = @[p.readTextValue]
|
result = @[p.readTextValue]
|
||||||
while seps.contains(p.peek): result.add(p.readTextValue(ignorePrefix = seps))
|
while seps.contains(p.peek): result.add(p.readTextValue(ignorePrefix = seps))
|
||||||
|
|
||||||
|
proc readBinaryValue(
|
||||||
|
p: var VCardParser,
|
||||||
|
isInline: bool,
|
||||||
|
propName: string
|
||||||
|
): string =
|
||||||
|
let value = p.readValue
|
||||||
|
if not isInline:
|
||||||
|
return value
|
||||||
|
|
||||||
|
try:
|
||||||
|
return base64.decode(value)
|
||||||
|
except ValueError:
|
||||||
|
p.error("invalid base64 value for the " & propName & " content type")
|
||||||
|
|
||||||
proc parseContentLines*(p: var VCardParser): seq[VC3_Property] =
|
proc parseContentLines*(p: var VCardParser): seq[VC3_Property] =
|
||||||
result = @[]
|
result = @[]
|
||||||
|
|
||||||
@@ -1035,12 +1049,13 @@ proc parseContentLines*(p: var VCardParser): seq[VC3_Property] =
|
|||||||
result.add(assignCommon(newVC3_Nickname(value = p.readValue)))
|
result.add(assignCommon(newVC3_Nickname(value = p.readValue)))
|
||||||
|
|
||||||
of $pnPhoto:
|
of $pnPhoto:
|
||||||
|
let isInline = params.existsWithValue("ENCODING", "B")
|
||||||
result.add(newVC3_Photo(
|
result.add(newVC3_Photo(
|
||||||
group = group,
|
group = group,
|
||||||
value = p.readValue,
|
value = p.readBinaryValue(isInline, "PHOTO"),
|
||||||
valueType = params.getSingleValue("VALUE"),
|
valueType = params.getSingleValue("VALUE"),
|
||||||
binaryType = params.getSingleValue("TYPE"),
|
binaryType = params.getSingleValue("TYPE"),
|
||||||
isInline = params.existsWithValue("ENCODING", "B")))
|
isInline = isInline))
|
||||||
|
|
||||||
of $pnBday:
|
of $pnBday:
|
||||||
let valueType = params.getSingleValue("VALUE")
|
let valueType = params.getSingleValue("VALUE")
|
||||||
@@ -1121,12 +1136,13 @@ proc parseContentLines*(p: var VCardParser): seq[VC3_Property] =
|
|||||||
result.add(assignCommon(newVC3_Role(value = p.readValue)))
|
result.add(assignCommon(newVC3_Role(value = p.readValue)))
|
||||||
|
|
||||||
of $pnLogo:
|
of $pnLogo:
|
||||||
|
let isInline = params.existsWithValue("ENCODING", "B")
|
||||||
result.add(newVC3_Logo(
|
result.add(newVC3_Logo(
|
||||||
group = group,
|
group = group,
|
||||||
value = p.readValue,
|
value = p.readBinaryValue(isInline, "LOGO"),
|
||||||
valueType = params.getSingleValue("VALUE"),
|
valueType = params.getSingleValue("VALUE"),
|
||||||
binaryType = params.getSingleValue("TYPE"),
|
binaryType = params.getSingleValue("TYPE"),
|
||||||
isInline = params.existsWithValue("ENCODING", "B")))
|
isInline = isInline))
|
||||||
|
|
||||||
of $pnAgent:
|
of $pnAgent:
|
||||||
let valueParam = params.getSingleValue("VALUE")
|
let valueParam = params.getSingleValue("VALUE")
|
||||||
@@ -1182,12 +1198,13 @@ proc parseContentLines*(p: var VCardParser): seq[VC3_Property] =
|
|||||||
result.add(assignCommon(newVC3_SortString(value = p.readValue)))
|
result.add(assignCommon(newVC3_SortString(value = p.readValue)))
|
||||||
|
|
||||||
of $pnSound:
|
of $pnSound:
|
||||||
|
let isInline = params.existsWithValue("ENCODING", "B")
|
||||||
result.add(newVC3_Sound(
|
result.add(newVC3_Sound(
|
||||||
group = group,
|
group = group,
|
||||||
value = p.readValue,
|
value = p.readBinaryValue(isInline, "SOUND"),
|
||||||
valueType = params.getSingleValue("VALUE"),
|
valueType = params.getSingleValue("VALUE"),
|
||||||
binaryType = params.getSingleValue("TYPE"),
|
binaryType = params.getSingleValue("TYPE"),
|
||||||
isInline = params.existsWithValue("ENCODING", "B")))
|
isInline = isInline))
|
||||||
|
|
||||||
of $pnUid:
|
of $pnUid:
|
||||||
result.add(newVC3_UID(group = group, value = p.readValue))
|
result.add(newVC3_UID(group = group, value = p.readValue))
|
||||||
@@ -1204,12 +1221,13 @@ proc parseContentLines*(p: var VCardParser): seq[VC3_Property] =
|
|||||||
result.add(newVC3_Class(group = group, value = p.readValue))
|
result.add(newVC3_Class(group = group, value = p.readValue))
|
||||||
|
|
||||||
of $pnKey:
|
of $pnKey:
|
||||||
|
let isInline = params.existsWithValue("ENCODING", "B")
|
||||||
result.add(newVC3_Key(
|
result.add(newVC3_Key(
|
||||||
group = group,
|
group = group,
|
||||||
value = p.readValue,
|
value = p.readBinaryValue(isInline, "KEY"),
|
||||||
valueType = params.getSingleValue("VALUE"),
|
valueType = params.getSingleValue("VALUE"),
|
||||||
keyType = params.getSingleValue("TYPE"),
|
keyType = params.getSingleValue("TYPE"),
|
||||||
isInline = params.existsWithValue("ENCODING", "B")))
|
isInline = isInline))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if not name.startsWith("X-"):
|
if not name.startsWith("X-"):
|
||||||
|
|||||||
@@ -153,6 +153,22 @@ suite "vcard/vcard3":
|
|||||||
serialized.contains("SOUND;ENCODING=b;TYPE=WAVE:" & payload)
|
serialized.contains("SOUND;ENCODING=b;TYPE=WAVE:" & payload)
|
||||||
serialized.contains("KEY;ENCODING=b;TYPE=PGP:" & payload)
|
serialized.contains("KEY;ENCODING=b;TYPE=PGP:" & payload)
|
||||||
|
|
||||||
|
test "spec: uri-backed binary properties 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"))
|
||||||
|
|
||||||
|
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: quoted parameter values are accepted":
|
test "spec: quoted parameter values are accepted":
|
||||||
let parsed = parseSingleVCard3(vcard3Doc(
|
let parsed = parseSingleVCard3(vcard3Doc(
|
||||||
"VERSION:3.0",
|
"VERSION:3.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user