fix: decode escaped colons in imported vcard notes #1438

This commit is contained in:
Linus Rath
2026-10-29 20:06:16 +01:00
parent a78d03a18d
commit e363427d0e
2 changed files with 33 additions and 5 deletions
+28
View File
@@ -220,6 +220,34 @@ describe("parseVCard", () => {
);
});
it("decodes the escaped colons Google Contacts writes (#1438)", () => {
const vcf = [
"BEGIN:VCARD",
"VERSION:3.0",
"FN:Test Example",
"NOTE:Reference\\: fictional value\\nSecond line\\, with comma\\; and semicolon",
"END:VCARD",
].join("\r\n");
const result = parseVCard(vcf);
expect(result[0].notes?.n0?.note).toBe(
"Reference: fictional value\nSecond line, with comma; and semicolon"
);
});
it("keeps an escaped backslash literal when an escapable character follows", () => {
const vcf = [
"BEGIN:VCARD",
"VERSION:3.0",
"FN:Paths",
"NOTE:C:\\\\new\\\\:dir\\\\\\:x\\\\\\\\",
"END:VCARD",
].join("\r\n");
const result = parseVCard(vcf);
expect(result[0].notes?.n0?.note).toBe("C:\\new\\:dir\\:x\\\\");
});
it("allows group kind without name or email", () => {
const vcf = [
"BEGIN:VCARD",
+5 -5
View File
@@ -204,12 +204,12 @@ function decodeQuotedPrintable(input: string, charset?: string): string {
}
}
// One pass, so an escaped backslash can't pair up with the next character.
// \: isn't a vCard escape, but Google Contacts writes it.
function decodeValue(raw: string): string {
return raw
.replace(/\\n/gi, "\n")
.replace(/\\,/g, ",")
.replace(/\\;/g, ";")
.replace(/\\\\/g, "\\");
return raw.replace(/\\([\\,;:nN])/g, (_, c: string) =>
c === "n" || c === "N" ? "\n" : c
);
}
function encodeValue(val: string): string {